oc中宏的使用

概述

宏,可以简单的理解为一个被赋予名字的代码段。当这个名字出现在代码中被使用的时候,就会被替换成相应的代码段。宏一般可以认为有两种存在(或者说是使用)形式,一种是类似于对象的宏,我们可以不那么严格的说成是宏对象,另一种是类似于方法的宏,我们姑且称之为宏方法。

宏对象

这种形式的宏,我们在使用的时候,可以把它当做一个简单的没有类型的数据源。
宏的创建形式也很简单,用#define指令,后面跟上宏的名字和被替换的数据源,比如:(需要注意的是宏的名称通常都是用大写字母)

#define TIMEOUT 30

getUserInfoRequest.timeOut = TIMEOUT;
modifyPsdRequest.timeOut = TIMEOUT;

使用的时候直接使用TIMEOUT就是常量30的意思,这是最基本的替换。

再比如:

#define UIKIT_FONT_LARGE [UIFont systemFontOfSize:16.0f]

firstLabel.font = UIKIT_FONT_LARGE;

这样的宏定义可以规范整个项目的UI,用起来也比较方便。

在宏定义中,如果要换行,使用""符号。

预处理时会认为""后面的还是在同一行,比如:

#define WEEKEND @[ \
@"Saturday", \
@"Sunday", \
]

NSArray *weekend = WEEKEND;

宏可作为一个判断条件,因为它本身可以认为是一段代码,故只需把判断条件定义为宏就行,如:

#define WIDTH_320     (CGRectGetWidth([[UIScreen mainScreen] bounds])==320)

if (WIDTH_320) {
    //屏宽为320时,to do something       
}else
{
    //TODO        
}

宏调用时,预处理器在替换宏的内容时,会继续检测内容本身是否也是宏定义,如果是,会继续替换内容。比如上面的那个宏:

#define SCREEN_WIDTH                     CGRectGetWidth([[UIScreen mainScreen] bounds])      //!< 屏幕宽度
#define WIDTH_320     (SCREEN_WIDTH==320)

将上面的WIDTH_320展开:

WIDTH_320   ==>     (SCREEN_WIDTH==320)
            ==>     (CGRectGetWidth([[UIScreen mainScreen] bounds])==320)

如果一个宏在展开时包含了自己的名字,则不再继续扩展,这样是为了防止无限递归。比如:

 #define x (4 + y)
 #define y (2 * x)

展开之后如下:

x    ==> (4 + y)
     ==> (4 + (2 * x))
 
y    ==> (2 * x)
     ==> (2 * (4 + y))

宏方法

我不严格地称宏方法,是因为这种看起来像是一种函数调用。它的定义方式与刚刚那种类似于对象的宏的定义相似,只是在宏名字后面紧跟着一对儿括号,如官方给的例子:

#define lang_init()  c_init()

 lang_init()  ==>  c_init()

再比如,React Native的源码中的一段宏定义:

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

我们扩展来看:

 RCT_EXPORT_MODULE(js_name)     ==>
 
 
 RCT_EXTERN void RCTRegisterModule(Class); 
 
 + (NSString *)moduleName 
 {
     return @#js_name; 
 }
 
 + (void)load 
 {
     RCTRegisterModule(self); 
 }

我们在调用RCT_EXPORT_MODULE()这个宏的时候,其实是调用了三个方法,RCTRegisterModule,moduleName,load,这三个方法具体在各个类里实现了哪些逻辑,我们不去深究,单单一个宏方法就全部囊括了。

上面出现了return @#js_name,其中js_name是传人宏方法中的参数,#符号的作用是将宏中的参数转化为字符串,比如传入的参数为commonModule,则将@#js_name展开后return的内容就是@"commonModule"

再比如,我现在有两个NSMutableDictionary字典,分别为paramsDic和params,我想把paramsDic里的部分数据加到params中,我们的代码可能会这么写:

[params setObject:paramsDic[@"keyName1"] forKey:@"keyName1"];
[params setObject:paramsDic[@"keyName2"] forKey:@"keyName2"];
[params setObject:paramsDic[@"keyName3"] forKey:@"keyName3"];
[params setObject:paramsDic[@"keyName4"] forKey:@"keyName4"];
[params setObject:paramsDic[@"keyName5"] forKey:@"keyName5"];

如果上面的代码,在赋值的时候,做一些判空置为空字符串或者其他判断,就会感觉每次写都写那么长的代码,遇到懒人,他可能用宏定义这么写(判断啥的直接放在宏定义里,我这里没写,仅作参考):

#define PARAMS_ADD(keyName) [params setObject:paramsDic[@#keyName] forKey:@#keyName]

PARAMS_ADD(keyName1);
PARAMS_ADD(keyName2);
PARAMS_ADD(keyName3);
PARAMS_ADD(keyName4);
PARAMS_ADD(keyName5);

还有一个符号##,它的作用是链接前后两个word。比如官方的例子:

 struct command
 {
   char *name;
   void (*function) (void);
 };
 
 struct command commands[] =
 {
   { "quit", quit_command },
   { "help", help_command },
   ...
 };

写成宏是这个样子的:

 #define COMMAND(NAME)  { #NAME, NAME ## _command }
 
 struct command commands[] =
 {
   COMMAND (quit),
   COMMAND (help),
   ...
 };

再比如:

#define DEFINE_SINGLETON_FOR_CLASS(className) \
\
+ (className *)sharedManager { \
static className *shared##className = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
@synchronized(self){ \
shared##className = [[self alloc] init]; \
} \
}); \
return shared##className; \
}

假如传人的参数是MyClass,上面的shared##className即为sharedMyClass;

多参数宏

一个宏可以接受数量可变的参数声明一个函数。比如官方给的例子:

 #define eprintf(...) fprintf (stderr, __VA_ARGS__)

宏方法声明时,括号里为...表明这种宏可接受数量可变的参数,宏调用时,其参数列表的所有命名参数,包括任何逗号,成为变量参数,替换标识符VA_ARGS出现出现的位置。因此,我们对上面这个宏方法使用后扩展如下:

 eprintf ("%s:%d: ", input_file, lineno)
 ==>  fprintf (stderr, "%s:%d: ", input_file, lineno)

再比如:

 #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

1. 
 eprintf("success!\n", );
      ==> fprintf(stderr, "success!\n", );

仔细看上面的1,是不是发现了什么,官方的描述是这样的:

This formulation looks more descriptive, but unfortunately it is less flexible: you must now supply at least one argument after the format string. In standard C, you cannot omit the comma separating the named argument from the variable arguments. Furthermore, if you leave the variable argument empty, you will get a syntax error, because there will be an extra comma after the format string.

大概说的是上面的那种宏定义方式不够灵活,说你要在格式化的字符串后面至少跟一个参数,逗号分隔符也不能省略。不然就像1的那样,后面跟着一个逗号和空格,当然解决办法还是有的,还是用上面提到的##,我们修改下宏定义,如下:

 #define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

再来看调用的展开:

 eprintf ("success!\n")
      ==> fprintf(stderr, "success!\n");

这里##的作用是连接前后两个字符时,发现后面的是空字符,就删掉前面的,分隔符。

取消宏定义

使用#undef指令可取消宏定义,#undef接受单个参数,也就是宏的名字。直接看官方例子:

 #define FOO 4
 x = FOO;        ==> x = 4;
 #undef FOO
 x = FOO;        ==> x = FOO;

需要说明一点,宏定义中的,空格出现在相同的地方,一个空格跟多个空格的并没有差别,比如:

#define fx(x)   (x + x)

等同于

#define fx(x)            (x         +          x)

最后,想了解更详细更官方的宏知识,请戳这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容