iOS开发Swift

iOS 闭包需要使用 weak self 的情况

什么是循环引用?


如图,A中有B,B中有A;则当我们初始化A之后,a.b和b.a形成了【引用循环】

这是我们不希望看到的,因为此时 A 和 B 两个类的实例都不能被 deinit

这样改写之后, A 和 B 两个类的实例就都可以被 deinit 了

因为我们在 var a前面加上了 weak,就是向编译器表明我们不希望持有 a

闭包需要使用 weak self 的情况


发生这种情况的主要原因是self持有了closure,而closure有持有了self,所以就造成了循环引用,从而小明对象没有被释放。

一句话解释:只有当block直接或间接的被self持有时,才需要weak self。

// 这种情况没必要
[self fetchDataWithSucess:^{
     [self doSomething];
}];

//这种情况就有必要
self.onTapEvent = ^{
    [self doSomething];
};

block中为什么会用到weakself是因为要避免循环引用,一旦出现循环引用那么对象就会常驻内存。如果一个应用程序里面你有很多循环引用,那么内存占用就会比较大,这当然是谁都不想看到的结果。那么问题的重点就是:什么时候会出现循环引用?再先来看一个例子:

NSArray *anArray = @[@"1", @"2", @"3"];
[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [self doSomething:idx];
}];

这种情况下,block中retain了self,当block中的代码被执行完后,self就会被ARC释放。所以不需要处理weakself的情况。

再来看一个例子

@interface aViewController ()
@property (nonatomic, strong) void(^aBlock)(id obj, NSUInteger idx, BOOL *stop);
@end

__weak aViewController *weakSelf = self;
self.aBlock = ^(id obj, NSUInteger idx, BOOL *stop) {
    [weakSelf doSomething:idx];
}

这个例子的区别在于:block被self strong引用。所以结果就是block中引用了self,self引用了block。那么这个时候,如果你不使用weakself,则self和block永远都不会被释放。

那么是不是遇到block都要使用weakself呢?当然不是,而且如果全部使用weakself,会出现你想执行block中的代码时,self已经被释放掉了的情况。

另外,在处理weakself时,有两种做法:__weak和__unsafe_unretained。两种做法各有推荐,有的人觉得后者从字面上更好理解,而有的人觉得前者更加安全,因为self被释放时会自动指向nil。有的人又说了,就是应该让app崩溃才能发现问题所在。这个,看个人吧。