根据定义,“宏是已命名的代码片段。每当使用该名称时,它就会被宏的内容替换”。我曾经在类中多次使用那些宏的代码,这些代码基本上是硬编码的字符串。
我的怀疑是:
为了解释我的第二个疑问,我的UIView中有一个名为“menuBurger.png”的图像文件。
[self.hamBurgerButton setImage:[UIImage imageNamed:@"menuBurger.png"] forState:UIControlStateNormal];
因此,在这种情况下,我可以通过两种方式创建宏
案例1:
#define HAMBURGER_BUTTON_IMAGE_NAME @"menuBurger"
案例2:
#define HAMBURGER_BUTTON_IMAGE [UIImage imageNamed:@"menuBurger"]
我在情况2中声明宏的方式有什么问题吗?使用宏代替对象(在第2种情况下,它返回UIImage对象)是一种好习惯吗?
最佳答案
1)何时使用宏
首先让我参考另一个StackOverflow答案:
宏是预处理器定义。这意味着之前
您的代码被编译,预处理器将扫描您的代码,并且
在其他地方,替换宏的定义
看到您的宏的名称。没有比这更聪明的事了
那。
参考:https://stackoverflow.com/a/20837836/4370893
基于此,在某些情况下,使用宏将使您的代码不仅更具可读性,而且不适合错误。我将列出其中一些:
1.避免字符串重复
第一种情况是您的程序必须处理不同类中的许多相似字符串,例如处理仅存在于程序中的字典时。
#define USERNAME_KEY @"username"
在上面的示例中,您具有一个带有
@"username"
字符串的宏,您可以使用它代替一直写@"username"
。它的好处之一是您将永远不必打错字。错误输入密钥名称已成为过去。有些人更喜欢使用
static const
,但是是否更好,则取决于您的需求。如果将static const
添加到.h
文件,则它将对导入该文件的任何类可用,并且仅分配一次。但是,如果需要在应用程序的多个部分中使用Macros或
static const
,则只需将它们添加到项目.pch
文件中即可。由于宏是在编译时被替换的,因此每次使用宏时都会分配它们,但是static const
会为您拥有的每个类分配,即使您不使用它也是如此。就像我之前说的,这取决于您的需求。2.多个编译选项
Marcos在编译时被替换,这意味着您可以使用它们在单个项目中创建应用程序的多个版本。在一个示例中,假设您的应用程序具有常规版本(兼容macOS 10.9+)和旧版本(macOS 10.6+)。维护两个项目会很糟糕,因此您可以使用宏来解决该问题。
#define IS_LEGACY_VERSION __MAC_OS_X_VERSION_MAIN_REQUIRED < __MAC_10_9
上面的行创建了一个
IS_LEGACY_VERSION
宏BOOL
宏,它告诉您项目是否需要低于macOS 10.9的版本。这使您可以在编译结果发生变化的不同情况下使用宏:#if IS_LEGACY_VERSION == TRUE
// Only in legacy app
#else
// Only in regular app
#endif
看到上面的其他if / else吗?在宏的条件下,它可以在任何地方使用,甚至可以在外部函数中使用,以使任何事情发生(甚至存在)。
当您想使用过去不支持的功能时,这特别有用,因此您需要添加一个全新的类来支持它,例如读取JSON。您可以将该函数添加到NSData中:
-(id)jsonObject
{
#if IS_LEGACY_VERSION == FALSE
return [NSJSONSerialization JSONObjectWithData:self options:0 error:nil];
#else
NSString* string = [[NSString alloc] iniWithData:self encoding:NSUTF8StringEncoding];
return [string jsonObject];
#endif
}
[NSString jsonObject]
函数来自SZJsonParser。您可以将两个文件都添加到项目中,将#if IS_LEGACY_VERSION == TRUE
添加到两个文件的开头,并将#endif
添加到它们的结尾。然后,您只需导入“SZJsonParser.h”即可完成! [NSData jsonObject]
函数在常规版本和旧版本中均可使用,在常规版本中没有SZJsonParser的痕迹,在旧版本中也没有NSJSONSerialization的痕迹。问题:您不能直接使用SZJsonParser吗?
当然可以,但是也许有一天我将不得不放弃该应用程序的旧版本,并且该应用程序的这一部分将不得不消亡。另外,Apple的NSJSONSerialization比SZJsonParser更优化,因此,如果我可以为普通版用户提供更好的体验,为什么不呢?
3.轻松替换字符串
想象一下,示例1中的字符串是来自JSON请求的密钥,该请求被转换为字典。如果有人确定密钥将不再是
"username"
,而是"name"
,那么替换它将非常容易。这也适用于URL,文件路径,主机以及甚至更复杂的对象(如颜色),但是您应该知道何时使用它。有了它,您可以使用所有应用程序URL创建
define
的列表,以便将它们全部放在一个地方,使它们更易于查找。 static const
也具有这一优势。4.合并以上的内容
如果结合以上三个示例给出的情况,则可能性是多种多样的。宏比看起来有用得多。
2)声明宏时的好与坏做法
我将使用您自己的案例有一个好的和坏的例子:
#define HAMBURGER_BUTTON_IMAGE_NAME @"menuBurger"
最佳实践:可以在应用程序中的任何位置按名称调用汉堡包按钮图像,如果更改名称,替换将非常容易。这也为变量提供了一个名称,这比直接调用
[UIImage imageNamed:@"menuBurger"]
更好(再次,请不要忘记static const
的说明)。#define HAMBURGER_BUTTON_IMAGE [UIImage imageNamed:@"menuBurger"]
不良做法:这属于您应用程序中的一部分逻辑,并将其隐藏在定义中,因此,这不是一件好事。在逻辑方面,您必须注意它们。我将举几个例子:
#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0]
最佳实践:您正在简化一个函数,其中没有隐式逻辑。
#define ScreenWidth [[UIScreen mainScreen] bounds].size.width
最佳实践:您将大型函数调用减少为
define
,这可能非常有用。但是,您应该小心。您只能将其添加到导入了UIKit的类中,因此这可能是一种危险的方法。#define DATE_COMPONENTS NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
最佳实践:由于这是一个带有约束条件的值,因此它将具有固定值。
#define NavBar self.navigationController.navigationBar
不良做法:使用
self
可能会导致您犯错,因为您将被限制为某种类。请记住,宏在编译时会被替换,因此self
将是不同的对象,具体取决于您使用它的位置。#define ApplicationDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
不良做法:如果您的 class 未导入AppDelegate,则会失败,这可能会导致您出错。
#define MAX(x, y) ((x) > (y) ? (x) : (y))
非常不好的做法: x和y都使用了两次,这可能会导致您遇到问题。您可以在此处找到有关该说明的解释:
http://weblog.highorderbit.com/post/11656225202/appropriate-use-of-c-macros-for-objective-c
宏使用的其他示例(不一定全部都是好的做法):
关于ios - 何时在Objective-C中使用宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42807404/