ios - 何时在Objective-C中使用宏

标签 ios objective-c macros

根据定义,“宏是已命名的代码片段。每当使用该名称时,它就会被宏的内容替换”。我曾经在类中多次使用那些宏的代码,这些代码基本上是硬编码的字符串。

我的怀疑是:

  • 何时使用宏
  • 声明宏
  • 时的好与坏做法

    为了解释我的第二个疑问,我的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_VERSIONBOOL宏,它告诉您项目是否需要低于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

    宏使用的其他示例(不一定全部都是好的做法):
  • https://www.tutorialspoint.com/objective_c/objective_c_preprocessors.htm
  • http://matt.coneybeare.me/my-favorite-macros-for-objective-c-development-in-xcode/
  • https://gist.github.com/numo16/3407652
  • http://amattn.com/p/useful_objective_c_macros.html
  • 关于ios - 何时在Objective-C中使用宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42807404/

    相关文章:

    scala - 如何在 scala 宏内部从带有类型参数的类型获取类型参数?

    ios - 以编程方式自动布局功能

    objective-c - 启动模态视图 Controller 来解决选项卡栏和导航 VC 的方向 "lock"?

    iphone - iPad 问题中的 NSDateFormatter

    macros - 动态地将参数传递给 freemarker 宏

    c++ - 获取当前命名空间和函数名(但不是完整签名)的宏?

    ios - 不同 Sprite Kit SKBlendMode 选项的方程式是什么?

    ios - 找不到 iOS 应用的应用私钥

    ios - 如何在信号链中发送错误

    objective-c - ConnectionKit还能用吗?