之前写过一篇attribute((constructor))用法探究,当时是在看代码的时候,对它产生了偶遇.而这几天,越发发现这个__attribute__
的强大.作为一个iOS开发者,我试着总结了一下这个在我们日常开发中的应用.
从 __Nonull说起
苹果在Xcode6.3中引入了这个特性, 目的在于为Swift混编时,让编译器知道这个Object-C对象是不是可选的.使用方法如下:
-(void)openPath:(NSString * _Nonnull)path;
今天在llvm的文档中,发现了一段这样的描述:
The nonnull attribute indicates that some function parameters must not be null, and can be used in several different ways. It’s original usage (from GCC) is as a function (or Objective-C method) attribute that specifies which parameters of the function are nonnull in a comma-separated list.
大概意思是:
nonnull
这个属性表示函数的参数不能为空,并且这个属性有几种不同的使用方式,最基本的用法就是用来修饰函数(或者OC的方法),使用一个用逗号分隔的参数表来表明函数或者方法的那个哪个参数非空.
还是代码比较明显:
-(void)openFile:(NSString*)file __attribute__((nonnull(1)));
当我们这么使用时候:
[self openFile:nil];
就会得到这么一个警告:
几个注意点:
- nonull的参数从1开始
- OC的隐含参数
self
和_cmd
不计入参数范围
_Nonnull
和_Nullable
在文档中也有说明:
_Nonnull
修饰指针类型,表示这个指针类型不会把null
当做有意义的参数,用法如下:
int fetch(int * _Nonnull ptr);
_Nullable
修饰指针类型,表示这个指针类型可以是null
的,用法如下:
int fetch_or_zero(int * _Nullable ptr);
注意:它们只能修饰指针类型的参数.
RAC中的 @onExit
用过RAC的应该对这个关键字不陌生,它实现了,当一个变量作用域结束时,调用指定的block,查看这个宏的定义:
#define onExit \
rac_keywordify \
__strong rac_cleanupBlock_t metamacro_concat(rac_exitBlock_, __LINE__) __attribute__((cleanup(rac_executeCleanupBlock), unused)) = ^
这个rac_keywordfiy
的定义如下:
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif
这也是为什么onExit
使用的时候,前面需要添加一个@
,因为只有这样才能凑成完整的@autoreleasepool
或者@try {}
.
言归正传,我们关注这个部分:
__attribute__((cleanup(rac_executeCleanupBlock), unused))
先说简单的,unused
表示函数或者变量可能不用,防止编译器产生警告.
而这个__attribute__((cleanup(...)))
用来修饰变量,当变量的作用域结束的时候,就调用参数.参数是一个函数,当然,也可以是block
,RAC里面就是这么干的.用代码来说明:
static void stringCleanUp(__strong NSString **string) {
NSLog(@"%@", *string);
}
static void cleanupBlock(__strong void(^*block)(void)) {
(*block)();
};
- (void)viewDidLoad {
[super viewDidLoad];
__strong NSString * myname __attribute__((cleanup(stringCleanUp), unused)) = @"kenny";
__strong void(^block)(void) __attribute__((cleanup(cleanupBlock), unused)) = ^{
NSLog(@"gonna released");
};
}
需要注意的是cleanup
的参数方法:它的参数是修饰的变量的地址.所以会用到了*block
和**string
,另外:
对于指向objc对象的指针(id *),如果不强制声明__strong
默认是__autoreleasing
,造成类型不匹配
参考链接: