Clang Attributes
iOS开发工作中,查看官方文档时经常见到各种系统宏定义,而定义宏时经常一堆以__attribute__(xx)
的语法格式出现,这些究竟是何方神圣,有何作用?本文摘出比较常见的,做一番解释。
以 __attribute__(xx)
的语法格式出现,是 Clang 提供的一些能够让开发者在编译过程中参与一些源码控制的方法。
__attribute__((format(__NSString__, F, A)))
格式化字符串
可以查看 NSLog 的用法:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
// Marks APIs which format strings by taking a format string and optional varargs as arguments
#if !defined(NS_FORMAT_FUNCTION)
#if (__GNUC__*10+__GNUC_MINOR__ >= 42) && (TARGET_OS_MAC || TARGET_OS_EMBEDDED)
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
#else
#define NS_FORMAT_FUNCTION(F,A)
#endif
#endif
__attribute__((deprecated(s)))
版本弃用提示
在编译过程中能够提示开发者该方法或者属性已经被弃用
/** Deprecated, use initWithTimeInterval: instead. */
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970
__attribute__((deprecated("Use initWithTimeInterval:")));
__attribute__((availability(os,introduced=m,deprecated=n, obsoleted=o,message=“” __VA_ARGS__)))
指明使用版本范围
os 指系统的版本,m 指明引入的版本,n 指明过时的版本,o 指完全不用的版本,message 可以写入些描述信息。
- (void)method __attribute__((availability(ios,introduced=3_0,deprecated=6_0,obsoleted=7_0,message=“iOS3到iOS7版本可用,iOS7不能用”)));
__attribute__((unavailable(…)))
方法不可用提示
这个会在编译过程中告知方法不可用,如果使用了还会让编译失败。
__attribute__((unused))
没有被使用也不报警告
__attribute__((__warn_unused_result__))
不使用方法的返回值就会警告,目前 swift3 已经支持该特性了。oc中也可以通过定义这个attribute来支持。
__attribute__((__availability__(swift, unavailable, message=_msg)))
OC 的方法不能在 Swift 中使用。
__attribute__((cleanup(…)))
作用域结束时自动执行一个指定方法
作用域结束包括大括号结束,return,goto,break,exception 等情况。这个动作是先于这个对象的 dealloc 调用的。
Reactive Cocoa 中有个比较好的使用范例,@onExit 这个宏,定义如下:
#define onExit \
rac_keywordify \
__strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^
static inline void rac_executeCleanupBlock (__strong rac_cleanupBlock_t *block) {
(*block)();
}
这样可以在就可以很方便的把需要成对出现的代码写在一起了。同样可以在 Reactive Cocoa 看到其使用
if (property != NULL) {
rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
if (attributes != NULL) {
@onExit {
free(attributes);
};
BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type;
BOOL isProtocol = attributes->objectClass == NSClassFromString(@“Protocol”);
BOOL isBlock = strcmp(attributes->type, @encode(void(^)())) == 0;
BOOL isWeak = attributes->weak;
shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol;
}
}
可以看出 attributes 的设置和释放都在一起使得代码的可读性得到了提高。
__attribute__((overloadable))
方法重载
能够在 c 的函数上实现方法重载。即同样的函数名函数能够对不同参数在编译时能够自动根据参数来选择定义的函数
__attribute__((overloadable)) void printArgument(int number){
NSLog(@“Add Int %i”, number);
}
__attribute__((overloadable)) void printArgument(NSString *number){
NSLog(@“Add NSString %@“, number);
}
__attribute__((overloadable)) void printArgument(NSNumber *number){
NSLog(@“Add NSNumber %@“, number);
}
__attribute__((objc_designated_initializer))
指定内部实现的初始化方法
如果是 objc_designated_initializer
初始化的方法必须调用覆盖实现 super 的 objc_designated_initializer
方法。
如果不是 objc_designated_initializer
的初始化方法,但是该类有 objc_designated_initializer
的初始化方法,那么必须调用该类的 objc_designated_initializer
方法或者非 objc_designated_initializer
方法,而不能够调用 super 的任何初始化方法。
__attribute__((objc_subclassing_restricted))
指定不能有子类
相当于 Java 里的 final 关键字,如果有子类继承就会出错。
__attribute__((objc_requires_super))
子类继承必须调用 super
声明后子类在继承这个方法时必须要调用 super,否则会出现编译警告,这个可以定义一些必要执行的方法在 super 里提醒使用者这个方法的内容时必要的。
__attribute__((const))
重复调用相同数值参数优化返回
用于数值类型参数的函数,多次调用相同的数值型参数,返回是相同的,只在第一次是需要进行运算,后面只返回第一次的结果,这时编译器的一种优化处理方式。
__attribute__((constructor(PRIORITY)))
和 __attribute__((destructor(PRIORITY)))
PRIORITY 是指执行的优先级,main 函数执行之前会执行 constructor,main 函数执行后会执行 destructor,+load 会比 constructor 执行的更早点,因为动态链接器加载 Mach-O 文件时会先加载每个类,需要 +load 调用,然后才会调用所有的 constructor 方法。
通过这个特性,可以做些比较好玩的事情,比如说类已经 load 完了,是不是可以在 constructor 中对想替换的类进行替换,而不用加在特定类的 +load 方法里。
Clang 警告处理
先看看这个
#pragma clang diagnostic push
#pragma clang diagnostic ignored “-Wdeprecated-declarations”
sizeLabel = [self sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
如果没有#pragma clang 这些定义,会报出 sizeWithFont 的方法会被废弃的警告,这个加上这个方法当然是为了兼容老系统,加上 ignored “-Wdeprecated-declarations” 的作用是忽略这个警告。通过 clang diagnostic push/pop 可以灵活的控制代码块的编译选项。
网友评论