五月的时候写过一篇讨论关于const内部链接性 的文章,结果稍不留神又犯了一个类似的错误:忽略了C++中对全局常量的特殊处理。
情景如下:在iOS中进行消息传递一般都采用NotificationCenter投递通知的方式进行,而一个Notification的唯一标识就是它的名字。为了减少不同文件间的依赖和耦合,决定把各个消息的名字以全局变量的方式分别定义到相应的.m或者.mm文件中,而不是像以前那样全写到.h内——后者虽然直观,但是有个最大的问题是一旦添加删除消息,就导致所有无关的代码单元重新编译一遍,简而言之:坑爹啊!
做法很简单,比如一个AViewController.m文件需要监听某个通知,而BViewController.m会post这个通知。于是在 AViewController.mm中是: > NSString* const kNotifyA = @”notify_a”; [NotificationCenter defaultCenter] addObserver:self selector:@selector(onNotifyA:) name:kNotifyA object:nil];
而BViewController.m中则: > extern NSString* const kNotifyA; [[NotificationCenter defaultCenter]postNotificationName:kNotifyA object:nil];
在.m中一切OK。但是在.mm文件中却会出现_kNotifyA not referenced之类的错误,链接失败。忙着写代码,于是做了些特殊处理了事。 回家后回顾了下原来那篇文章顺带google了一把,才想明白:C和C++中对于全局常量的处理是有差别的。(.mm就是为了和C++混编特地改的后缀,写objc的童鞋懂的…)如同我那篇文章中所讲extern和const同时使用的时候是会使得变量具有外部连接属性。但是在cpp或者mm文件中定义了的常量变量是具有内部链接属性的(或者说是静态链接,static link),换言之,它对于其他编译单元是不可见的。于是上文中的BViewController.o文件跑去找链接时就找不到kNotifyA这个定义,于是链接失败。
解决方法很简单,大致有三种:
MS的原文: One way to resolve this error is to include the const initializations in a header file and include that header in your CPP files when necessary, just as if it was function prototype. Another possibility is to make the variable non-constant and use a constant reference when assessing it.