根据定义“宏是一个已经给出名称的代码片段。每当使用该名称时,它将被宏”的内容替换。我曾经在我的班级中多次使用的代码中使用宏,基本上是硬编码的字符串。
我的怀疑是:
为了解释我的第二个疑问,我的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对象)?
答案 0 :(得分:8)
1)何时使用宏
让我先引用另一个StackOverflow答案:
宏是预处理器定义。这意味着以前 你的代码被编译,预处理器扫描你的代码,然后 其他的东西,无论在哪里,都可以替代宏的定义 看到你的宏的名字。它没有做任何比聪明更聪明的事情 这一点。
参考:https://stackoverflow.com/a/20837836/4370893
基于此,在某些情况下,使用宏将使您的代码不仅更具可读性,而且不太适合于错误。我将列出其中一些:
<强> 1。避免字符串重复
第一种情况是你的程序必须在不同的类中处理许多类似的字符串,比如当你处理只存在于程序中的字典时。
#define USERNAME_KEY @"username"
在上面的示例中,您有一个带有@"username"
字符串的宏,您可以使用该字符串,而不是一直编写@"username"
。它的一个好处是你永远不必处理拼写错误。错误地写出关键名称将成为过去的事情。
有些人更喜欢使用static const
,但如果它更好或没有,则取决于您的需求。如果您将static const
添加到.h
文件,则任何导入它的类都可以使用它,并且只会分配一次。
但是,如果您需要在应用的多个部分中使用宏或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&#39; s。您可以将该功能添加到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
添加到它们的末尾。然后你只需导入&#34; SZJsonParser.h&#34;并做了! [NSData jsonObject]
函数适用于常规版本和旧版本,常规版本中没有SZJsonParser的痕迹,旧版本中没有NSJSONSerialization的痕迹。
问题:你不能直接使用SZJsonParser吗?
当然,但也许有一天我将不得不放弃应用程序的Legacy版本,这部分应用程序将不得不死掉。此外,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])
糟糕的做法:如果您的班级没有导入失败的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
宏的其他使用示例(不一定都是好的做法):