<p>http://pingguohe.net/2011/09/01/howtojudgewhetheradelegateisreleased/#comment-983</p>
<p>困惑了相当长时间的一个问题了,实际上在Xcode4中会出现?</p>
<p>if ((int)delegate->isa == classIsa) {?</p>
<p>这行报错,member reference base type 'id<
HTTPRequestDelegate>' is not a structure or union</p>
<p>因为ide不认为它是NSObject对象,只要对它转为NSObject对象即可。</p>
<p>//************************************************************************************</p>
<p><span style="color: #333333; font-family: 'Trebuchet MS', Helvetica, Arial, Geneva, sans-serif; font-size: 22px; line-height: 26px;">Cocoa中回调delegate的方法时判断delegate是否已经被释放</span><span style="color: #333333; font-family: 'Lucida Grande', Verdana, Helvetica, Arial, Geneva, sans-serif; font-size: 13px; line-height: 20px;">
</span></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">这个需要是因为最近在做网络请求的底层,需要在请求完成时回调某delegate的某方法。<br style="padding: 0px; margin: 0px;">然而回调时经常遇到这种情况:delegate已经被release了。如果delegate已经被dealloc掉,则无法调用其方法,否则引起程序crash。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">此篇文章中博客作者也有相同的问题:<a style="color: #5371c5; text-decoration: none; padding: 0px; margin: 0px;" href="http://longtimenoc.com/archives/objective-c-delegate%E7%9A%84%E9%82%A3%E7%82%B9%E4%BA%8B%E5%84%BF">http://longtimenoc.com/archives/objective-c-delegate的
那些事儿</a></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">首先,我们此时无法用if (nil = delegate)判断delegate是否已经被dealloc掉,因为被dealloc之后,delegate对象也不是空的,大部分情况下是一个objc_object*类型的C指针。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">其次,我们又会想到在本对象中先对delegate retain一次,这样回调时不会崩溃了。但是这样会出现一个retain cycle,本对象和delegate都永远不会被释放了。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">再次,我想到是否可以用isKindOfClass判断是否被dealloc。然而此时也不能用[delegate isKindOfClass]判断是否已经被dealloc,因为isKindOfClass是NSObject
协议中的方法,此时delegate如果不是NSObject,对其发送isKindOfClass消息会导致crash。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">此时很小部分情况下,delegate会是NSObject,可能是NSDictioary,也可能是原本的类。而大部分情况下,delegate已经不是NSObject。所以此时任何形式的[delegate method]都会导致crash,因为任何的[delegate method]的前提都是:delegate是一个NSObject。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">无奈之下我又想到,使用delegate->isa判断delegate是不是NSObject。这里介绍一下,objective-c中所有对象都是结构体,每个结构体中都有一个名为isa的指针指向其类。而类也是一种结构体,类的isa指向其父类。处于最底层的结构体是无isa的,NSObject的isa指向的也是NSObject。isa具体的值是运行时确定的。<br style="padding: 0px; margin: 0px;">一开始的思路是用delegate->isa->isa->isa->…一直指下去,如果isa与NSObject的isa相同,则说明delegate是一个NSObject。但是这样是行不通的,因为如果delegate不是NSObject,只是objc_object*,一直指下去却指不到NSObject的话,总会指到最底层的结构体,而此结构体无isa,如果访问结构体内没有的东西,程序又会crash了。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">说了这么多,结论就是这个问题很是蛋疼。再做不出来我就要把释放本对象的
责任交给用户了。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">等等,如果在本对象初始化后,delegate传进来时保存delegate的isa,此时delegate一定未被dealloc(为什么?因为是单
线程的),在回调时判断delegate此时的isa和当时保存的isa是否一样,就可以解决了。<br style="padding: 0px; margin: 0px;">代码如下:<br style="padding: 0px; margin: 0px;">协议声明:</p>
<div class="wp_syntax" style="margin-top: 0px; margin-right: 0px; margin-bottom: 1.5em; margin-left: 0px; color: #110000; background-color: #f9f9f9; width: 670px; padding: 0px; border: 1px solid silver;">
<div class="code" style="padding-top: 2px; padding-right: 4px; padding-bottom: 2px; padding-left: 4px; vertical-align: top; margin: 0px;">
<pre class="c">@protocol HTTPRequestDelegate <span style="color: #339933; padding: 0px; margin: 0px;"><</span>NSObject<span style="color: #339933; padding: 0px; margin: 0px;">></span>
?
@optional
?
<span style="color: #339933; padding: 0px; margin: 0px;">-</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">void</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span> requestDidLoadResponse<span style="color: #339933; padding: 0px; margin: 0px;">:</span><span style="color: #009900; padding: 0px; margin: 0px;">(</span>NSString <span style="color: #339933; padding: 0px; margin: 0px;">*</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>responseDictionary<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #339933; padding: 0px; margin: 0px;">-</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">void</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span> requestDidFailedLoadResourceURLWithError<span style="color: #339933; padding: 0px; margin: 0px;">:</span><span style="color: #009900; padding: 0px; margin: 0px;">(</span>NSError <span style="color: #339933; padding: 0px; margin: 0px;">*</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>error<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
?
@end</pre>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">类声明:</p>
<div class="wp_syntax" style="margin-top: 0px; margin-right: 0px; margin-bottom: 1.5em; margin-left: 0px; color: #110000; background-color: #f9f9f9; width: 670px; padding: 0px; border: 1px solid silver;">
<div class="code" style="padding-top: 2px; padding-right: 4px; padding-bottom: 2px; padding-left: 4px; vertical-align: top; margin: 0px;">
<pre class="c">@interface MyClass <span style="color: #339933; padding: 0px; margin: 0px;">:</span> NSObject <span style="color: #339933; padding: 0px; margin: 0px;"><</span>FooDelegate<span style="color: #339933; padding: 0px; margin: 0px;">,</span>BarDelegate<span style="color: #339933; padding: 0px; margin: 0px;">></span>
<span style="color: #009900; padding: 0px; margin: 0px;">{</span>
...
<span style="color: #993333; padding: 0px; margin: 0px;">int</span> classIsa<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
id <span style="color: #339933; padding: 0px; margin: 0px;"><</span>HTTPRequestDelegate<span style="color: #339933; padding: 0px; margin: 0px;">></span> delegate<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
?
@property <span style="color: #009900; padding: 0px; margin: 0px;">(</span>nonatomic<span style="color: #339933; padding: 0px; margin: 0px;">,</span>assign<span style="color: #009900; padding: 0px; margin: 0px;">)</span> id <span style="color: #339933; padding: 0px; margin: 0px;"><</span>HTTPRequestDelegate<span style="color: #339933; padding: 0px; margin: 0px;">></span> delegate<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
?
@end</pre>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">类实现:</p>
<div class="wp_syntax" style="margin-top: 0px; margin-right: 0px; margin-bottom: 1.5em; margin-left: 0px; color: #110000; background-color: #f9f9f9; width: 670px; padding: 0px; border: 1px solid silver;">
<div class="code" style="padding-top: 2px; padding-right: 4px; padding-bottom: 2px; padding-left: 4px; vertical-align: top; margin: 0px;">
<pre class="c">@implementation MyClass
?
@synthesize delegate<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
?
<span style="color: #339933; padding: 0px; margin: 0px;">-</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span>id<span style="color: #009900; padding: 0px; margin: 0px;">)</span>initWithDelegate<span style="color: #339933; padding: 0px; margin: 0px;">:</span><span style="color: #009900; padding: 0px; margin: 0px;">(</span>id<span style="color: #009900; padding: 0px; margin: 0px;">)</span>requestDelegate
<span style="color: #009900; padding: 0px; margin: 0px;">{</span>
self <span style="color: #339933; padding: 0px; margin: 0px;">=</span> <span style="color: #009900; padding: 0px; margin: 0px;">[</span>super init<span style="color: #009900; padding: 0px; margin: 0px;">]</span><span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #b1b100; padding: 0px; margin: 0px;">if</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span>self<span style="color: #009900; padding: 0px; margin: 0px;">)</span> <span style="color: #009900; padding: 0px; margin: 0px;">{</span>
<span style="color: #666666; font-style: italic; padding: 0px; margin: 0px;">//TODO:Send request,etc.</span>
self.<span style="color: #202020; padding: 0px; margin: 0px;">delegate</span> <span style="color: #339933; padding: 0px; margin: 0px;">=</span> requestDelegate<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
<span style="color: #b1b100; padding: 0px; margin: 0px;">return</span> self<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
?
<span style="color: #339933; padding: 0px; margin: 0px;">-</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">void</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>setDelegate<span style="color: #339933; padding: 0px; margin: 0px;">:</span><span style="color: #009900; padding: 0px; margin: 0px;">(</span>id<span style="color: #339933; padding: 0px; margin: 0px;"><</span>HTTPRequestDelegate<span style="color: #339933; padding: 0px; margin: 0px;">></span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>iDelegate
<span style="color: #009900; padding: 0px; margin: 0px;">{</span>
delegate <span style="color: #339933; padding: 0px; margin: 0px;">=</span> iDelegate<span style="color: #339933; padding: 0px; margin: 0px;">;</span>
NSString <span style="color: #339933; padding: 0px; margin: 0px;">*</span>delegateDescription <span style="color: #339933; padding: 0px; margin: 0px;">=</span> <span style="color: #009900; padding: 0px; margin: 0px;">[</span><span style="color: #009900; padding: 0px; margin: 0px;">[</span>iDelegate class<span style="color: #009900; padding: 0px; margin: 0px;">]</span> description<span style="color: #009900; padding: 0px; margin: 0px;">]</span><span style="color: #339933; padding: 0px; margin: 0px;">;</span>
classIsa <span style="color: #339933; padding: 0px; margin: 0px;">=</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">int</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>objc_getClass<span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #009900; padding: 0px; margin: 0px;">[</span>delegateDescription UTF8String<span style="color: #009900; padding: 0px; margin: 0px;">]</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span><span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
?
<span style="color: #339933; padding: 0px; margin: 0px;">-</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">void</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>callback
<span style="color: #009900; padding: 0px; margin: 0px;">{</span>
<span style="color: #b1b100; padding: 0px; margin: 0px;">if</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #993333; padding: 0px; margin: 0px;">int</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span>delegate<span style="color: #339933; padding: 0px; margin: 0px;">-></span>isa <span style="color: #339933; padding: 0px; margin: 0px;">==</span> classIsa<span style="color: #009900; padding: 0px; margin: 0px;">)</span> <span style="color: #009900; padding: 0px; margin: 0px;">{</span>
<span style="color: #b1b100; padding: 0px; margin: 0px;">if</span> <span style="color: #009900; padding: 0px; margin: 0px;">(</span><span style="color: #009900; padding: 0px; margin: 0px;">[</span>delegate respondsToSelector<span style="color: #339933; padding: 0px; margin: 0px;">:</span>@selector<span style="color: #009900; padding: 0px; margin: 0px;">(</span>requestDidLoadResponse<span style="color: #339933; padding: 0px; margin: 0px;">:</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span><span style="color: #009900; padding: 0px; margin: 0px;">]</span><span style="color: #009900; padding: 0px; margin: 0px;">)</span> <span style="color: #009900; padding: 0px; margin: 0px;">{</span>
NSString <span style="color: #339933; padding: 0px; margin: 0px;">*</span>responseString <span style="color: #339933; padding: 0px; margin: 0px;">=</span> @<span style="color: #ff0000; padding: 0px; margin: 0px;">"foobar"</span><span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">[</span>delegate requestDidLoadResponse<span style="color: #339933; padding: 0px; margin: 0px;">:</span>responseString<span style="color: #009900; padding: 0px; margin: 0px;">]</span><span style="color: #339933; padding: 0px; margin: 0px;">;</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span>
<span style="color: #009900; padding: 0px; margin: 0px;">}</span></pre>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">然而由于多线程的原因(发出请求和回调发生是在两个线程上的),会有极少数的情况(测试中发生概率在万分之一以内,和CPU有关)在if ((int)delegate->isa == classIsa)判断时,delegate当前的isa会和本对象初始化时isa相等,也就是说delegate未被dealloc,而调用回调时,delegate已被dealloc,导致程序crash。避免这种小概率事件的方法是,在delegate中发送请求前[self retain]一下,然后在回调到达时[self release]一下,这样除了避免崩溃以外,还会确保请求已经发送完毕,不会被发送一半。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">以上。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding: 0px;">–OpenThread</p>
<p>?</p>