RAC中使用@weakify和@strongify解决block使用过程中的弱引用和强引用的问题
我们在使用时比较简单:
@weakify(self);
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
... ...
}];
RAC中的宏定义的比较深,关于这两个关键字,完全可以拿YYCategoriesMacro.h中的代码解析,来看一下YY中的代码:
/**
Synthsize a weak or strong reference.
Example:
@weakify(self)
[self doSomething^{
@strongify(self)
if (!self) return;
...
}];
*/
#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif
#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif
我们在此先不考虑是不是DEBUG环境,先把这个宏翻译过来并用普通代码书写,做个示例:
- (IBAction)buttonClick:(id)sender {
self.redView = [[RedView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
__weak __typeof__(self) weak_self = self;
// __weak __typeof(&*self) weak_self = self; // OK
// __weak typeof(self) weak_self = self; // OK
// __weak id weak_self = self; // OK
self.redView.callback = ^{
// #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
__typeof__(self) self = weak_self;
ViewController2 *controller = [[ViewController2 alloc] init];
[self presentViewController:controller animated:YES completion:nil];
};
}
然后把我们的这个普通的常见写法做成宏就是上面宏的写法,只不过为了支持@,在宏中使用了一些技巧。但是为什么YY和RAC的宏里面都有看似多余的autoreleasepool
和try{} ...
代码呢?首先这个autoreleasepool和try的前面没有加@,所以我们在用的时候加上,形如:@weakify(self) & @strongify(self)
, 其次RAC官方给出的解释是:
Details about the choice of backing keyword:
The use of @try/@catch/@finally can cause the compiler to suppress return-type warnings. The use of @autoreleasepool {} is not optimized(优化) away by the compiler,resulting in superfluous(过多的) creation of autorelease pools.
Since neither option is perfect, and with no other alternatives, the compromise is to use @autorelease in DEBUG builds to maintain compiler analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary autorelease pools.
我们来分析一下,大致意思是说:使用@try/@catch/@finally 会导致编译器忽略返回值警告,而使用@autoreleasepool并不会被编译器优化,这样的话,使用@autoreleasepool{}会使程序创建过多的autorelease pool, 使用这两种方案都不是非常完美的,但是又没有其他方式,所以折中的做法是在DEBUG模式下使用@autorelease从而保持编译器的分析,在非DEBUG模式下使用@try/@catch从而避免插入过多的autorelease pool.
这段话还是比较好理解的,有个小困惑是 使用@try/@catch/@finally 会导致编译器忽略返回值警告
,这句话的意思可以用一小段代码来解释:
我们来写一个block,用于判断传入的对象是不是Foo的实例:
int main(int argc, const char * argv[]) {
@autoreleasepool {
id foo = [[NSObject alloc] init];
@autoreleasepool {}
BOOL (^isEqualFoo)(id) = ^BOOL(id object) {
}; // 编译器提示错误:Control reaches end of non-void block
NSLog(@"%d", isEqualFoo(foo));
}
return 0;
}
图片.png
这是可以理解的,因为这是一个“返回值为BOOL, 跟一个id参数的block”,但是在block的内部却没有返回BOOL值,编译器提示错误,也就是说,我们必须返回一个BOOL,有趣的是,我们可以通过抛出一个异常让编译通过:
int main(int argc, const char * argv[]) {
@autoreleasepool {
id foo = [[NSObject alloc] init];
@autoreleasepool {}
BOOL (^isEqualFoo)(id) = ^BOOL(id object) {
@throw [NSException exceptionWithName:@"" reason:nil userInfo:nil]; // 编译器没有报错
};
NSLog(@"%d", isEqualFoo(foo));
}
return 0;
}
不仅如此,如果我们在这个block内部使用@try/@cath或@try/@finally语法,编译器同样不报错误,成功通过:
int main(int argc, const char * argv[]) {
@autoreleasepool {
id foo = [[NSObject alloc] init];
@autoreleasepool {}
BOOL (^isEqualFoo)(id) = ^BOOL(id object) {
@try {} @finally {} // 编译通过,没毛病
// return object == foo;
};
NSLog(@"%d", isEqualFoo(foo));
}
return 0;
}
这就是上面RAC解释说“The use of @try/@catch/@finally can cause the compiler to suppress return-type warnings.”的意思。
这种情况是Clang编译器底层做的处理,照老外的话说“weird”,在搜查了资料之后找到了"why"的原因:
网友评论