self.block = ^{
[self myDescription];
};
在block语句块中,如果需引用self,而self对象中又持有block对象,就会造成循环引用 循环引用(retain cycle) ,导致内存泄露.
一般我们是这么解决的,使用一个 __weak 修饰的weakSelf变量指向self对象,在block中使用weakSelf
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf myDescription];
};
这样写仍然会出问题,因为weakSelf是弱引用,而self一旦释放了,weakSelf可能为nil,比如下面
1 定义一个MyObj对象,它的属性有一个block对象
@interface MyObj : NSObject
@property (nonatomic, copy)void(^block)();
@end
@implementation TestObj
- (void)dealloc {
NSLog(@"%s",__func__);
}
- (instancetype)init {
self = [super init];
if (self) {
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"%@",weakSelf);
});
};
}
return self;
}
@end
2,在其他类中定义一个myFunc方法
- (void)myFunc{
MyObj *obj = [MyObj new];
obj.block();
}
执行 myFunc 方法,结果是打印的是(null),因为block里打印的方法是异步执行的,在 NSLog(@”%@”,weakSelf); 这句代码执行之前 myFunc 函数就结束,所以 obj 对象已经被release了。
怎么解决呢?所以再对 weakSelf 做一次 __strong 就可以了:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"%@",strongSelf);
});
};
}
使用了 __strong 在 strongSelf 变量作用域结束之前,对 weakSelf 有一个引用,防止对象(self)提前被释放。而作用域一过, strongSelf 不存在了,对象(self)也会被释放。
- 问题
每次都要写那两条长长的 __weak 和 __strong ,而且在block里用到的self的全部要改成strongSelf,假设把一段很多self的代码拷贝到block里,一个个改成strongSelf就很麻烦
@weakify(self);
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
@strongify(self);
[self popViewControllerAnimated:YES];
}];
只要在block外用了@weakify(self);然后再block里写@strongify(self);就可以了,@strongify(self);语句后的的self可以原封不动.
@weakify、@strongify 这两个神奇的宏最终替换了什么东西。
@weakify(self)
@autoreleasepool{} __attribute__((objc_ownership(weak)))__typeof_(self) self_weak_ = (self)
@strongify(self)
__attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_;
@autoreleasepool {} 是什么?
注意到@weakify(self)中, @ 并不属于宏的一部分,当然你不能平白无故写个@对吧,所以RAC的weakify宏定义机智地给你补了一句 autoreleasepool {} 这样一前一后就变成了啥事都没干的@autoreleasepool {}
__attribute__((objc_ownership(weak)))是什么?
这个就是__weak在编译前被编译器替换的结果,而weakify这个宏最终替换的代码包含有__weak(后面说到),所以编译器再替换就成了__attribute__((objc_ownership(weak)))
weakify、strongify的定义
... 和 __VA_ARGS__
看下 NSLog 和 printf ,他们的传入参数有多个,用...表示不确定参数个数,看看NSLog的定义:
NSLog(NSString *format, ...)
在宏里也可以用 ... 来表示多个参数,而 __VA_ARGS__ 就对应多个参数的部分。
举个例子,你觉得NSLog太难看,想造一个自己的log打印函数,比如MHFLog你就可以这么写:
#define MHFLog(...) NSLog(__VA_ARGS__)
宏连接符 ## :
## 这个符号 会将出现在 ## 左右两边连接的东西起来,举个例子:
宏定义为 #define XLink(n) x ## n ,这宏的意思是把x和传入的n连接起来书写:
#define XLink(n) x ## n
int x1 = 1;
int x2 = 2;
int x3 = 3;
//打印x1 x2 x3
NSLog(@"%d",XLink(1)); //NSlog(@"%zd",x1);
NSLog(@"%d",XLink(2)); //NSlog(@"%zd",x2);
NSLog(@"%d",XLink(3)); //NSlog(@"%zd",x3);
typeof(self) 在block代码块内这样写为什么不会造成循环引用(retain cycle)?
typeof 、 __typeof 、 __typeof__ 这三种写法功能是一样的,这三个函数并不是标准C中规定的,它是一种编译器特性。
/**
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