基础说明:
oc一般可以采用声明protocol-delegate的形式进行回调,
声明协议P,A类遵循协议<P>,B类声明一个指针id<P> delegate;
然后通过A类的方法a调用B类的方法b,b方法中再self.delegate 调用协议方法,就实现了回调;
有一种情况就要注意OC对象的释放时机,一般情况下
1 2 3 4 |
- (void) test { A * a = [[A alloc]init]; [a print]; } |
print函数执行完,a就释放了。
如果print是个耗时操作,就会等一会儿。
下面改成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//主函数中new个A - (void) main { A * a = [[A alloc]init]; [a test]; } A异步执行B的start方法,start方法中会用self.delegate调用A的方法 - (void) test { b = [[B alloc]init]; b.delegate = self; dispatch_async(dispatch_get_global_queue(0, 0), ^{ [b start]; }); } //B类中的start方法通过协议调用A的方法 - (void) start { sleep(3); [self.delegate methodA]; } |
大家都知道要把delegate属性设成weak,在这种情况下,异步进入B的start方法,A的test已经执行完毕,就会被释放。b的delegate就成了空,methodA也不会执行了。
把delegate属性改成strong是一个办法,但诸多文章教你不要用strong的delegate。
另一种办法是给A加一个成员变量B * _b;也就是A持有B的实例,B没执行完A也不会释放,此时delegate必须要用weak。
回调的另一实现方法是使用block:
1 2 3 4 5 6 7 8 9 |
- (void) test { b = [[B alloc]init]; __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(0, 0), ^{ [b startBlock:^{ [weakSelf test]; }]; }); } |
很多人无法区分何时使用
__weak typeof(self) weakSelf = self;
像上面代码的情况,没有互相持有的类,其实直接使用self即可,而且也不会出现提前释放的情况。
如果A包含了B的对象,就要用weakSelf,因为有时传进去的block会被B赋值给成员变量。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
typedef void(^Ablock)(); @implementation B { Ablock ablock; } //如果在B类中,把传进去的block赋值给了B的一个成员变量 - (void) startBlock:(Ablock)completion { ablock = completion; if (completion) { completion(); } } |
如果A调用时还是用self,就会出现循环引用,A无法释放,B也无法释放的情况。使用weakSelf总不会出错的,也不会出现提示释放的问题。
总而言之,循环引用就是,两个以上的变量,互相strong的持有,这个环的一节设成weak就打破了。
避免循环引用的同时还要注意A类提前释放,B的回调无法执行的情况。