delegate 之assign & retain讨论
作者QQ:415074476
QQ群:191280586
delegate 的property属性到底该用assign 还是retain?
网上有说法是该用assign.
但我在实际工作中时常遇到crash的情况(包括umeng,sian,tencent sdk)。另外,在cocoa的api中,异步方法调用时,delegate的retainCount会+1,说明,在这种情况下,其实是使用的retain.
本课题就是结合官方文档,官方api测试。自由Delegate设计测试,来给出一个最终答案。
首先谈谈网上的看法。
现在网上都是认为应该是assign. 避免循环计数。
这个很容易理解的。。
案例一:
对象A 持有对象B.
对象B有个delegate属性,指向的对象是对象C.
假设delegate为retain的,对象C是对象B本身。
描述下引用计数:
对象A 持有对象B.(对象B计数为1)
对象B有个delegate属性(retain),指向的对象是对象B本身.(对象B的计数为2).
Ok.现在对象A释放。调用dealloc方法。释放对象B。(对象B的引用计数减低为1).
然后呢?没有然后了。
因为delegate的释放是在对象B调用dealloc方法时。
-(void)dealloc
{
….
[delegate release];
[super dealloc];
}
明显对象B这个对象占的内存泄露了。
如果有读者用过umeng的Sdk,就会发现他们是怎么处理的呢。
在对象A的dealloc方法做了处理, 不光处理了对象B本身,还把他的delegate置为了nil. 否则会crash哦。
好吧。我给的这个案例。不把delegate置为nil,也不会crash的,只会有内存泄露。
大家使用cocoa api时,会在dealloc方法里,去把delegate置为nil么?不会。
所以,这种折中的方法显然行不通。挖苦点是太山寨,太不安全。
到底该如何设计?
既然上面的例子用retain不行,是不是说明网上的说法是正确的。Delegate的属性必须为assign, 不然会造成内存泄露。
好吧。我给出另一个案例:
对象A 持有对象B.
对象B持有 对象C.
对象C有个delegate属性(assign), 指向对象B 。delegate声明了方法callMe.
对象B实现方法callMe.
一般情况下, 以上代码不会有问题。
但在有异步调用就容易出问题了。
假设对象C 有个方法是start. 发起一个异步请求,获取到资源后,在调用callMe方法。
调用栈如下:
[B.C start];
[C.delegate callMe];
由于异步的关系。上述两行代码之间会发生什么事呢?
可能对象B释放掉了,并且它的内存被其它对象所占用。
那么
[C.delegate callMe]一定crash.
怎么办?加上if(delegate && [delegate respondsToSelector:@selector(callMe)])
{
//doSometing
}
很遗憾地告诉你。 这样是真不行, 照样crash.
怎么办?既然assign会因为对象的失去而crash, 那我retain呗。 问题回去了,又是内存泄露。
不信?看看:
对象A 持有对象B.(对象B引用计数1)
对象B持有 对象C.
对象C有个delegate属性(assign), 指向对象B.( 对象B引用计数2).
对象A调用dealloc.
对象B引用计数减少为1.
然后呢?没然后了。。泄露。
因为必须对象B引用计数减少为0时,才会调用dealloc方法。才会release对象C。然后对象在dealloc方法里release delegate.
都行不通啊。。
我认为assign是没错的。是下面的判断方法不对。
if(delegate && [delegate respondsToSelector:@selector(callMe)])
{
//doSometing
}
到底该如何判断呢?
NSObject协议有下面两个方法。
– (BOOL)conformsToProtocol:(Protocol *)aProtocol;
– (BOOL)respondsToSelector:(SEL)aSelector;
NSObject对象有下面这个方法:
– (void)doesNotRecognizeSelector:(SEL)aSelector;
到底哪个方法是有效的呢?
一一测试下吧。
– (BOOL)respondsToSelector:(SEL)aSelector;
都不可行。
因为占据内存的可能是一个基本数据类型。
看来得分情况了。
一:同步方法用assign.
二:异步方法。在发请求时retain,响应时release. 这种情况就不应该用property属性。看下官方的异步方法里要传delegate参数的Api,是不是在头文件里,找不到delegate的property属性呢。
怎么设置呢? 传参。启动方法里传参。 这才是正确的方法。
有不同看法的,欢迎讨论。
一个关于网络,SEO,PHP,服务器管理的网络杂货铺。 http://www.yujunzhuo.com 网络杂货铺