iOS常量使用extern和static原理探究,为什么Defines中默认推荐使用extern?



一、场景需求

我们在项目架构中,每个模块下都会需要一个相关定义文件。例如:注册、登录、忘记密码为一个Account模块,模块下的枚举、宏定义、通知、数据存储 Key 等都放在一个EHIAccountDefines文件中。对于宏定义、通知、数据存储 Key 等常量的定义使用我们来讨论一下。

二、使用方法

2.1 宏定义

// EHIAccountDefines.h
#define kEHIChangedPasswordNotification @"UserChangedPassword";

最省事的方式就是宏定义了,但是宏定义本质只是文本替换。在预编译器影响速度、不能声明变量类型、不能使用 const 修饰导致使用中值可以更改,所以常量的定义不推荐。一般用于方法的定义(方法也可以使用内联函数inline代替一部分,具体使用不多赘述)。

2.2 static

// EHIAccountDefines.h
static NSString *const kEHILogoutNotification = @"UserDidLogout";

这种写法也是很常见的,不止是在这个场景,也有很多用在.m中定义一个动画时长、cell复用池名称的定义等。

2.3 extern

// EHIAccountDefines.h
extern NSString *const kEHILoginNotification;

// EHIAccountDefines.m
NSString *const kEHILoginNotification = @"UserDidLogin";

排除#define的写法。extern是各个博客和书上的默认写法,没有太多对此的解释,好像理应如此。而实际上static的话只需要.h文件即可,更加方便。所以这次探究一下为什么还都默认使用extern呢?

三、探究

3.1 举个例子

.pch引用的全局的一个Defines文件中写下:

// .h
extern NSString *const kEHIConstExternStr;

static NSString *const kEHIConstStaticStr = @"一个静态字符串";
// .m
NSString *const kEHIConstExternStr = @"一个外部字符串";

在几个ViewControllerviewDidLoad中使用这两个字符串(注意是分别几个文件中写,不是基类):

NSLog(@"%@ %@:%p, %x", self.className, kEHIConstExternStr, kEHIConstExternStr, &kEHIConstExternStr);
NSLog(@"%@ %@:%p, %x", self.className, kEHIConstStaticStr, kEHIConstStaticStr, &kEHIConstStaticStr);

%p打印的是这个字符串的值的地址,后面%x是存放这个变量的地址。
只有值类型是直接放的值,引用类型放的都是地址。字符串是引用类型,故而它地址存放的是它的值地址。

3.2 输出结果

[1982:519479] AppDelegate 一个外部字符串:0x106715780, 6692cd8
[1982:519479] AppDelegate 一个静态字符串:0x1066e2020, 6669410

[1982:519479] EHIHomeContantViewController 一个外部字符串:0x106715780, 6692cd8
[1982:519479] EHIHomeContantViewController 一个静态字符串:0x1066e2020, 667b080

[1982:519479] EHIHomeViewController 一个外部字符串:0x106715780, 6692cd8
[1982:519479] EHIHomeViewController 一个静态字符串:0x1066e2020, 667d530

查看输出结果,可以得知:

extern:变量地址和值地址都是同一个。
static:值地址是同一个,但是变量地址是不同的。

那么 static 在所有地方引用,是在所有地方都分配一个变量地址呢,还是只有使用到才会分配呢?反编译查看:

static NSString *const kShowGuideKey = @"kShowGuideKey01";

static 的变量地址是变的,但是在用到的地方才会创建。三个文件里有用到,就会自动生成三个变量,分别是_kShowGuideKey_kShowGuideKey_101980440_kShowGuideKey_1019949c0,都指向同一个字符串@“kShowGuideKey01”值。
编译的时候第一次用到是原名加下划线前缀,只有再有用到会追加地址最为后缀区分。

extern 的不管多少个地方用到,编译时只有一个下划线前缀的变量,因为 extern 的只有一个.m有这个定义。

3.3 分析

extern:全局只有一个变量,对一个常量字符串的引用,只会被初始化一次。
static:全局有多个变量,每个引用的地方的变量都会初始化一次,分配内存空间,对一个常量字符串的引用(所以很多博客和书根本就没提这样写)。

3.3.1 static 的原理

而为什么会有这个结论呢?static 修饰的变量为什么多次分配内存呢?

上面的使用,如果既不用 extern 修饰,也不用 static 修饰呢?答案是报错,因为重复定义了。
我们先了解下 .h 的作用:

.h文件可以说没有用,只是给外部看的。h中是实际上无法定义变量的,编译.m的时候会把.h中的内容在.m中展开后编译才会真正的生成一个变量。

所以,没有任何修饰符的话相当于多个类中都定义了同一个变量名,就重复定义报错了。

然而,这里加上static的话,就不会报错,只是每个文件使用的变量不一样。也就是说 static 限制了该变量作用域在每个使用它的文件内。

我们可以得到结论:

static:限制作用域。

(这里就不扩展说它修饰局部变量、全局变量、函数的作用了~~~ )

3.3.2 extern 的原理

接下来,为什么 extern 可以保持变量和常量一致呢?

我们看个例子,日常开发中在 .m 中使用 static 的情况很多,例如:

static NSString *const kEHIShowGuideKey = @"EHIShowGuideKey";

如果不加 static 修饰符呢?

// ClassA.m
NSString *const kEHIShowGuideKey = @"EHIShowGuideKey";

系统会自动给我们加上 extern 修饰为外部变量。按照上面说的 static 限制作用域的原理,我们在其他地方就不能重复定义这个变量名称了。但是它现在是一个外部变量,我们可以在其它地方使用这个变量。

如果其他地方使用,我们可以这么用:

// ClassB.m
extern NSString *const kEHIShowGuideKey;

ClassB 在使用时,声明我在使用一个外部变量。但是这样就要每个使用该字符串的时候都去声明下。

所以常规的写法还是如下:

// ClassA.h
extern NSString *const kEHIShowGuideKey;

// ClassA.m
NSString *const kEHIShowGuideKey = @"EHIShowGuideKey";

我们可以得到结论:

extern:外部的,作用就是修饰后面的内容是由外部定义的而已。

四、总结

终于把思路理清楚写完了!😭

了解了原理再看各种情况就清晰明了了。还没有一下就明白的小伙伴自己写写例子、消化下哈。

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

推荐阅读更多精彩内容