如何解决ios delegate 用法已经释放引起的crash

再谈Cocoa中回调delegate的方法时判断delegate是否已经被释放
我在中描述了如何使用delegate的isa判断其所属类是否改变,从而判断delegate是否被释放。但是Nike指出:一旦此delegate注册过KVO,其isa就会被改变了。除非保证delegate不会被注册KVO(这要求判断delegate是否被释放时保证delegate必须是我们自己创建的,而不是其他任何人,而且我们要手工保证此delegate不会注册KVO),否则使用此种方法判断理论和实际上都是不可靠的。虽然iphone上的YAJL库目前也采用了这种方式判断对象类型,但是自从知道了KVO的那些事儿后,我觉得isa的方式还是很不靠谱的。
周末闲来无事读了读,发现objc的runtime中有两种判断类型的方式比较靠谱,他们可以直接取得任意一个objc_object(和id是完全一样的数据类型)的类或者类名。其函数如下:
//Returns the class name of a given object.
const char *object_getClassName(id obj);
//Returns the class of an object.
Class object_getClass(id object);
第一个函数可以返回任意一个id的类名,第二个函数可以返回任意一个id的Class。
而第二个函数会牵扯到isa-swizzling问题,参考八楼Nike大神的回复。
object_getClassName会不会牵扯到isa-swizzling问题我还没试过,没时间尝试~
以下为使用object_getClass判断delegate是否存在的一个例子,例子中MasterViewController将创建一个新线程试图回调DetailViewController的callback方法(涉及isa-swizzling,所以KVO下无效,但是下面例子的代码我没时间修改了)。
首先声明好用于回调的Protocol:
@protocol CallbackProtocol &NSObject&
- (void)callback;
然后在MasterViewController的成员变量里加上一个Class变量,用于存放delegate刚刚被设置进来时其Class:
@interface MasterViewController : UITableViewController
__unsafe_unretained id &CallbackProtocol& _delegate;
Class _originalClass;
在设置delegate的同时,将其Class保存在_originalClass成员变量中:
_originalClass = object_getClass(_delegate);
在回调时首先判断_delegate是否为空,若不为空再判断其类型是否改变,若类型已改变,则此对象已被释放(在中已经有相关叙述)。
if (_delegate != nil) {
Class currentClass = object_getClass(_delegate);
(currentClass == _originalClass)
[_delegate callback];
NSLog(@&orginal:%d present:%d - Not same class&,(int)_originalClass,(int)currentClass);
NSLog(@&Delegate is nil&);
若成员变量不保存Class类型而仅仅将其指针保存为int则性能更优。
刚刚提到object_getClass没有被手动声明的话编译时此函数会被warning。为此我们需要自己手动声明此函数。不管你写在哪,我是写在了.m里。
在用了object_getClass的.m的#import语句之下,@implemention语句之上,加上下面两句话:
//Returns the class of an object.
Class object_getClass(id object);
至此大功告成。
回调demo下载地址:,demo中首先由master view进入detail view,再由detail view返回master view,返回后开辟新的线程,新的线程在3秒之后判断detail view是否存在。
–OpenThread
[...] 然而如Nike的评论中所说,delegate一旦注册KVO,其isa是会改变的,所以如果我们不能手工保证其不会注册KVO,则使用此方法是不可靠的。使用其他运行时方法请参考这篇文章再谈Cocoa中回调delegate的方法时判断delegate是否已经被释放 [...]
object_getClassName(id object)不可用, 我试过了, 不知道你是怎样作的. 我的环境是iOS 5.0, 只有object_getClass有效!
手动声明一下试试看亲
亲测有效,不过没有很广泛的测试,谢谢楼主。
我的应用场景是这样的:我的一个view对model对象添加了观察者,当model的属性改变时view就会自动更新自己的内容。当view的dealloc方法被调用的时候我将从model中移除这个观察者,这个时候就会出现无法断定model是否已经释放的情况。
楼主,刚又测试了下,这种情况下的object_getClass()取得的class在KVO还是会改变的,这就又回到了您上一篇说到的isa问题
这样的问题,个人认为只会出现在多线程中,比较合适的做法是在dealloc中将delegate = nil;
看的出,作者是个有钻研精神的开发者。
但非常遗憾,object_getClass
并不会隐藏 Objective-C 的 IsA-swizzling 特性,所以实际上用object_getClass并不会有任何改进。另一方面说的,程序保存的delegate可能在释放之后分配给其它Objective-C对象,也可能释放之后完全变成一个完全无意义的值,如果这种情况你调用了object_getClass,将导致不可预期的结果。
我还是那句话:当我们遇到判断Delegate有效性出现困难时,往往应该想想我们是不是已经滥用了Delegate。
当你回头看你过去写的东西,觉得非常幼稚时,说明你已经进步了。作者加油!
话说object_getClassName会涉及isa-swizzling不?~~~Nike大神求邮箱~
object_getClassName当然涉及isa-swizzling。
似乎你没有仔细看我的前两次回帖。实际上你在通过你的方法判断某个指针对应的类之前,你首先要保证你保存的指针还是一个有效的Objective-C对象。但你目前并没有做这方面的考虑和判断。而且虽然利用Objective-C运行时特性可以有方法判断一个指针是否是一个Objective-C对象,但是效率不会太高,用于判断delegate的有效性并不划算。
我还是想告诉你:如果你的程序很容易出现由于delegate失效而导致程序Crash,除了考虑代码质量之外,还要考虑是不是滥用了delegate。
原来还有方法判断一个指针是否为objective-c对象,谢谢Nike指点~这几个月中对于这个问题的处理一直在采取ASIHTTP的方式,销毁自身前取消自身依赖的请求。而当时很想用运行时特性的原因是自动判断比手动取消回调优雅一些。至于效率问题,除非有大批创建销毁的动作,应该影响不会太大~而且也应该有方法用池和重用的方式避免大规模创建销毁
总之,感谢大神指点
你在这个问题上走偏了。正确的做法就是你所说的“销毁自身前取消自身依赖的请求。” 你所谓的优雅方法在我看来类似于发现程序crash了,不去查找真正的原因,而是希望加个try - catch能够避免crash。
请教大神们,NSURLConnection中的delegate如果已经被释放,NSURLConnection是如何避免crash的?
了解了~感谢指点
“NSURLConnection是如何避免crash的?” +1
我来回答Lost首先提出的问题:NSURLConnection 中的 delegate 如果已经被释放,NSURLConnection 是如何避免 crash 的?
其实 Apple 的 Cocoa 框架程序员很清楚,NSURLConnection 没有什么好办法保证NSURLConnection中的delegate对象已经被销毁(注意 我说的是销毁 dealloc,不是你说的释放)的时候,程序不crash。所以他们设计NSURLConnection是要 retain delegate 对象的。
对于这点:请看文档:
Special Considerations
The connection retains delegate. It releases delegate when the connection finishes loading, fails, or is canceled.
其实对于 NSURLConnection 的 delegate 实现是比较特殊的,不是简单的在 connection 中直接调用 delegate 对象的方法,因为 connection 和 delegate 运行在不同的线程里,所以这是一个需要借助于 runloop 的线程间 delegate调用。
博主大神 有上面Nike大神的微博或者博客的信息么
@numb:我从来不写技术博客,以为我发现用中文写的技术博客往往错漏百出,如果我写也好不到哪里去,呵呵。我有个腾讯微博:yudayu
[...] 但是delegate一旦注册KVO,其isa是会改变的,所以如果我们不能手工保证其不会注册KVO,则使用此方法是不可靠的。使用其他运行时方法请参考这篇文章再谈Cocoa中回调delegate的方法时判断delegate是否已经被释放 以上。 [...]
Select Month
August 2013 &(1)
June 2013 &(2)
April 2013 &(1)
March 2013 &(1)
February 2013 &(1)
June 2012 &(2)
May 2012 &(2)
December 2011 &(1)
November 2011 &(4)
September 2011 &(7)
August 2011 &(6)
July 2011 &(1)
June 2011 &(2)IOS Crash总结 - 推酷
IOS Crash总结
1、ARC中,对于调用私有函数调用中,返回值是void 或者参数本身是基本类型的,如果使用了id,因为ARC中会对参数和返回值进行retain,所以都会产生objc_retain的crash。
delloc函数是异步的:当对对象调用release的时候,即使该对象的retaincount = 0,
该对象的delloc函数也不是同步调用的。
例子:UIWebViewWk的destory函数的原因。
2、通用的情景是:观察者使用的时候。
A做为B的观察者向下传递,B中的C会回调到A中方法。
传统的A的delloc方法中销毁B,B的delloc方法中销毁C。在OC中由于
对象release之后,delloc方法是异步调用的,A delloc之后,B的retainCount = 0,但是当B的delloc函数还没有
调用的时候,C发生回调,就会因为A的野指针crash。
这个时候就要实现B的destory方法来销毁C。
这个destory方法在A的delloc方法中进行调用。这样就保证了A delloc的时候C也delloc了。
3、docmentView上面加入手势,网页内容发生改变,如页内的视频跳转之后,手势发生crash.
4、panGesture手势没有调用touchMove,而初始化的代码放在了touchMove中,导致变量没有初始化,成为了野指针
5、属性对象没有retain,delloc的时候crash.
以下是更改的具体案例:
7、crash发生在C代码中,很难追踪到栈:bilsonzhou 3.0以前
0 libsystem_kernel.dylib 0x3432132c __pthread_kill (in libsystem_kernel.dylib) 8
1 libsystem_c.dylib 0x32e1829e abort (in libsystem_c.dylib) 94
2 libc abi.dylib 0x37589f6a abort_message (in libc abi.dylib) 46
3 libc abi.dylib 0x3758734c _ZL17default_terminatev (in libc abi.dylib) 24
4 libobjc.A.dylib 0x3692b36e _objc_terminate (in libobjc.A.dylib) 170
5 libc abi.dylib 0x _ZL19safe_handler_callerPFvvE (in libc abi.dylib) 76
6 libc abi.dylib 0x operator delete(void*) (in libc abi.dylib) 0
7 libc abi.dylib 0x __cxa_current_exception_type (in libc abi.dylib) 0
8 libobjc.A.dylib 0x objc_exception_rethrow (in libobjc.A.dylib) 12
9 CoreFoundation 0x35d6450c CFRunLoopRunSpecific (in CoreFoundation) 404
10 CoreFoundation 0x35d6436c CFRunLoopRunInMode (in CoreFoundation) 104
11 GraphicsServices 0x322e8438 GSEventRunModal (in GraphicsServices) 136
12 UIKit 0x3634ccd4 UIApplicationMain (in UIKit) 1080
13 MttHD 0x0000377e main (in MttHD) (main.m:15)
这个栈已经存在很长时间了,根本的Crash原因是对dictionary插入了nil对象。
为什么栈变得这么丑,找不到挂的地方?
因为这句代码写在了C代码中:
void appScoreDataManager_Init(); //初始化评分管理信息
// whetherScoredFlag
NSNumber *numberObject = [g_AppScoreDataManager objectForKey:WHEHTER_SCORED_FLAG];
if (nil == numberObject)
[g_AppScoreDataManager setObject:MTTWhetherScoredFlag forKey:WHEHTER_SCORED_FLAG];
// APP_USED_COUNT
numberObject = [g_AppScoreDataManager objectForKey:APP_USED_COUNT];
if (nil == numberObject)
[g_AppScoreDataManager setObject:MTTAppUsedCount forKey:APP_USED_COUNT];
由于if语句中的代码很难被执行,所以一直没有发现。这个地方如果MTTWhetherScoredFlag不是等于0的话,会产生一个警告,
我们将warning作为error之后,这个地方就会编译不通过,而恰恰这个地方MTTWhetherScoredFlag = 0。被当成了一个nil对象,
所以不会产生warning导致编译不通过。
8、释放一个autorelease对象:bilsonzhou 3.0以前
#0 0x3374ec98 in objc_msgSend ()
#1 0x3702bc36 in CFGetRetainCount ()
#2 0x34dc9c0e in CA::release_root_if_unused ()
#3 0x34dc9bba in x_hash_table_remove_if ()
#4 0x34da8f9c in CA::Transaction::commit ()
#5 0x34da2054 in CA::Transaction::observer_callback ()
#6 0x3706aa34 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#7 0x in __CFRunLoopDoObservers ()
#8 0x3706d75a in __CFRunLoopRun ()
#9 0x36ffdec2 in CFRunLoopRunSpecific ()
#10 0x36ffddca in CFRunLoopRunInMode ()
#11 0x3195641e in GSEventRunModal ()
#12 0x319564ca in GSEventRun ()
#13 0x30ff5d68 in -[UIApplication _run] ()
#14 0x30ff3806 in UIApplicationMain ()
#15 0x00003ede in main (argc=1, argv=0x2fdff880)
at /Users/bilsonzhou/Desktop/QQBrowserIPAD/src/MttHDBrowser_iPad/MttHD/MttHD/main.m:15
该bug在5.0以上系统很难重现,在4.3.3系统上面随机出现;
从这个栈看,没有我们的代码,也是鸟都看不出一个。
这是离线阅读引进的bug,由于时间较长,相关同事也离职,查找原因时花了较长时间。
通过4.3真机调试时发现了如下系统日志:
modifying layer that is being finalized - 0xd332410
基本上锁定为某个view进行了重复释放,通过对离线阅读中释放的对象一一排查,
最后锁定为:
m_btnDel = [UIButton buttonWithType:UIButtonTypeCustom];
在delloc中
[m_btnDel release];
这种crash虽然原因很简单,但是由于随机性和crash栈的信息有限,查找的难度很大。
9、内存被写坏导致随机crash,crash堆栈如下 3.0以前
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 WebKit 0x393cfe62 -[WebView(WebPrivate) _loadBackForwardListFromOtherView:] + 130
1 MttHD 0x -[UIWebViewWK loadUrlFromOtherWK:] (UIWebViewWK.mm:4741)
2 MttHD 0x -[UIMttBrowserView preloadUrls:] (UIMttBrowserView.m:6341)
3 Foundation 0x __NSFireDelayedPerform + 446
4 CoreFoundation 0x32ace854 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 12
5 CoreFoundation 0x32ace4fe __CFRunLoopDoTimer + 270
6 CoreFoundation 0x32acd172 __CFRunLoopRun + 1226
7 CoreFoundation 0x32a40238 CFRunLoopRunSpecific + 352
8 CoreFoundation 0x32a400c4 CFRunLoopRunInMode + 100
9 GraphicsServices 0x365fb336 GSEventRunModal + 70
10 UIKit 0x UIApplicationMain + 1116
11 MttHD 0x main (main.m:15)
12 MttHD 0xx1000 + 10548
从上面crash线程的堆栈很难找到问题,再往下下面其它线程的堆栈;
Thread 16 name: Dispatch queue: com.apple.root.low-priority
Thread 16:
0 libsystem_kernel.dylib 0x3ae66e30 mach_msg_trap + 20
1 libsystem_kernel.dylib 0x3ae66fd0 mach_msg + 48
2 CoreFoundation 0x32ace2b6 __CFRunLoopServiceMachPort + 126
3 CoreFoundation 0x32acd02c __CFRunLoopRun + 900
4 CoreFoundation 0x32a40238 CFRunLoopRunSpecific + 352
5 CoreFoundation 0x32a400c4 CFRunLoopRunInMode + 100
6 CFNetwork 0x327a1612 CFURLConnectionSendSynchronousRequest + 330
7 Foundation 0x334353da +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 242
8 MttHD 0x0019d76c __30-[MttSubscribeImage pullImage]_block_invoke_0 (MttSubscribeImage.m:310)
9 MttHD 0x0019eaca __41-[MttSubscribeTaskQueue dispatch:forKey:]_block_invoke_075 (MttSubscribeTaskQueue.m:161)
10 libdispatch.dylib 0x3ad9d790 _dispatch_call_block_and_release + 8
11 libdispatch.dylib 0x3ada1652 _dispatch_root_queue_drain + 274
12 libdispatch.dylib 0x3ada17d4 _dispatch_worker_thread2 + 88
13 libsystem_c.dylib 0x3adc57ee _pthread_wqthread + 358
14 libsystem_c.dylib 0x3adc5680 start_wqthread + 4
发现一个可疑的线程,该线程的堆栈正好跑完并等待中,恰好是有程序本身的代码,通过review堆栈中的代码发现,response变量没有初始化。
NSHTTPURLResponse *
self.data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error: nil];
为什么没有初始化会导致crash呢?因为sendSynchronousRequest 的 returningResponse变量是out型变量,则函数会把结果写入returningResponse变量中,
当response没有初始化的时候系统默认会给它一个随机内存,sendSynchronousRequest 函数将内容写入这块随机内存程序以后的运行很可能会导致随机crash。
10、对象实例调用自身函数release自身实例导致crash 3.0以前
在MttVideoView类对象实例的dismissVideoView函数同步调用了closeVideoView将自身实例从UI树移除并release自身,从而导致随机crash,crash的堆栈
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0xc0000008
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x3358bc98 objc_msgSend + 16
1 CoreFoundation 0x36e33cd6 CFRetain + 62
2 CoreFoundation 0x36e3a176 +[__NSArrayI __new::] + 54
3 CoreFoundation 0x36e38a6e -[__NSPlaceholderArray initWithObjects:count:] + 122
4 QuartzCore 0x34c01db6 -[CALayerArray copyWithZone:] + 42
5 CoreFoundation 0x36e39b4a -[NSObject(NSObject) copy] + 10
6 UIKit 0x30e2bd7c -[UIView dealloc] + 52
7 MttHD 0x001e83e8 -[MttVideoView dealloc] (MttVideoView.m:378)
8 CoreFoundation 0x36e34c3c -[NSObject(NSObject) release] + 24
9 CoreFoundation 0x36e3519a CFRelease + 62
10 CoreFoundation 0x36e3aba2 -[__NSArrayM dealloc] + 86
11 CoreFoundation 0x36e34c3c -[NSObject(NSObject) release] + 24
12 CoreFoundation 0x36e3519a CFRelease + 62
13 CoreFoundation 0x36e37eb4 _CFAutoreleasePoolPop + 140
14 Foundation 0x33725bae NSPopAutoreleasePool + 2
15 Foundation 0x337aa73c __NSFireDelayedPerform + 472
16 CoreFoundation 0x36ea7a40 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 8
17 CoreFoundation 0x36ea9ec4 __CFRunLoopDoTimer + 844
18 CoreFoundation 0x36eaa83e __CFRunLoopRun + 1082
19 CoreFoundation 0x36e3aebc CFRunLoopRunSpecific + 224
20 CoreFoundation 0x36e3adc4 CFRunLoopRunInMode + 52
21 GraphicsServices 0x GSEventRunModal + 108
22 GraphicsServices 0x GSEventRun + 56
23 UIKit 0x30e32d62 -[UIApplication _run] + 398
24 UIKit 0x30e30800 UIApplicationMain + 664
25 MttHD 0x main (main.m:15)
26 MttHD 0x 0x1000 + 10160
这个是手动点击删除小窗口按钮退出小窗口的,结合代码分析,删除这个实例的堆栈必须有函数closeVideoView ,但是从crash的堆栈上看,没有发现函数closeVideoView的堆栈,
觉得很诡异,不符合堆栈逻辑。为什么会出现上面的堆栈呢?是因为该实例的函数调用栈还没有完成退出就调用自身的dealloc函数将自己删除掉了,
导致栈上的地址出现随机错误。
解决总结:要删除实例自身,应该要用异步延时删除,不能同步删除。
11、加入手势的对象发生了变化 bilsonzhou 3.0以前
Thread 2 Crashed:
0 libsystem_kernel.dylib 0x33db3a1c __pthread_kill + 8
1 libsystem_c.dylib 0x34aa73b4 pthread_kill + 52
2 libsystem_c.dylib 0x34a9fb78 __abort + 80
3 libsystem_c.dylib 0x34a9fc18 abort + 104
4 libstdc++.6.dylib 0x35470a64 __gnu_cxx::__verbose_terminate_handler() + 376
5 libobjc.A.dylib 0x32ce706c _objc_terminate + 104
6 libstdc++.6.dylib 0x3546ee36 __cxxabiv1::__terminate(void (*)()) + 46
7 libstdc++.6.dylib 0x3546ee8a std::terminate() + 10
8 libstdc++.6.dylib 0x3546ef5a __cxa_throw + 78
9 libobjc.A.dylib 0x32ce5c84 objc_exception_throw + 64
10 CoreFoundation 0x -[__NSArrayM objectAtIndex:] + 178
11 CoreFoundation 0x365ba130 -[NSMutableArray removeObject:range:identical:] + 284
12 CoreFoundation 0x365b9ffa -[NSMutableArray removeObject:] + 46
13 UIKit 0x305e31fe -[UIView(UIViewGestures) removeGestureRecognizer:] + 54
14 UIKit 0x305e3004 -[UIWebSelectionAssistant setGestureRecognizers] + 48
15 UIKit 0x305e4408 -[UIWebSelectionAssistant setEnabled:] + 48
16 UIKit 0x30664c70 -[UIWebDocumentView loadRequest:] + 168
17 CoreFoundation 0x3662b79c __invoking___ + 60
18 CoreFoundation 0x365a3436 -[NSInvocation invoke] + 102
19 WebCore 0x35c29c36 _ZL11SendMessageP12NSInvocation + 10
20 WebCore 0x35c29c0e _ZL15HandleAPISourcePv + 66
21 CoreFoundation 0x365ffa72 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 6
22 CoreFoundation 0x __CFRunLoopDoSources0 + 376
23 CoreFoundation 0x __CFRunLoopRun + 224
24 CoreFoundation 0x36592ebc CFRunLoopRunSpecific + 224
25 CoreFoundation 0x36592dc4 CFRunLoopRunInMode + 52
26 WebCore 0x35c2827e _ZL12RunWebThreadPv + 382
27 libsystem_c.dylib 0x34aa730a _pthread_start + 242
28 libsystem_c.dylib 0x34aa8bb4 thread_start + 0
这是个随机的bug,栈里面也没有我们的代码。幸好4.3系统在腾讯视频发生页内跳转之后该crash高概率出现了。
最后发现原因是;网页的双指单击手势加到了documentView上面,发生页内跳转之后documentView的内容发生了变化。在网页释放的时候就挂了。
修改方法是把手势加到了网页的scroll上面。
12、手势crash. bilsonzhou 3.0以前 3.1修复
0 libobjc.A.dylib 0x3b41c692 0x3b70
1 MttHD 0x000fce4c -[GestureDetectWindow handleOneFingerPan:] (in MttHD) (GestureDetectWindow.m:237)
2 UIKit 0x354a6d88 0x3112
3 UIKit 0x 0x284
4 UIKit 0xx2216
5 UIKit 0xx46
6 UIKit 0xx14
7 UIKit 0x 0x98
8 UIKit 0x3539bdb2 0x22
9 UIKit 0xx36
10 UIKit 0xx70
11 GraphicsServices 0x 0x18
12 GraphicsServices 0x 0x42
13 CoreFoundation 0xx334bf000 618866
14 CoreFoundation 0xx334bf000 618774
15 CoreFoundation 0xx334bf000 614296
16 CoreFoundation 0x334c7ebc 0x334bf000 36540
17 CoreFoundation 0x334c7d48 0x334bf000 36168
18 GraphicsServices 0x370912ea 0x26
19 UIKit 0x353dd300 0x120
20 MttHD 0x0000331e main (in MttHD) (main.m:15)
该crash在以前版本一直有上报,3.0更改手势机制之后上报量突然增大。
在3.0灰度版本的时候就有上报,但是上报的栈指向的是MttBrowserView成了野指针。如果
browserview都成了野指针那整个浏览器都野了,因此灰度之后排查了browserView中成员变量。对照3.0以前相关crash附近手势的代码进行了更改。
结果发出去之后还是有大量的上报。后来通过打印大量日志后发现,panGesture手势的三个过程:
touch begin,move,end。不是每次调用都会进入move。而我们的变量初始化都放在了move当中,move没有调用导致变量没有初始化野指针crash.
这个地方日志上报的栈中的行数并不是确定的。
13、NSRange Exception crash bilsonzhou 3.2引入
0 CoreFoundation 0x36dde29e __exceptionPreprocess + 158
1 libobjc.A.dylib 0x3738697a objc_exception_throw + 26
2 CoreFoundation 0x36dde1c0 +[NSException raise:format:] + 100
3 Foundation 0x32ab175e -[NSData(NSData) subdataWithRange:] + 174
4 MttHD 0x002300bc -[MTTCommandTunnel _send] (MTTCommandTunnel.m:194)
5 MttHD 0x -[MTTCommandTunnel streamHasSpaceAvailable:] (MTTCommandTunnel.m:126)
6 MttHD 0x0023025e -[MTTCommandTunnel stream:handleEvent:] (MTTCommandTunnel.m:239)
7 CoreFoundation 0x36d7b7ca _signalEventSync + 70
8 CoreFoundation 0x36d8161e _cfstream_solo_signalEventSync + 70
9 CoreFoundation 0x36d7b502 _CFStreamSignalEvent + 322
10 CFNetwork 0x CoreWriteStreamCFStreamSupport::coreStreamWriteEvent(__CoreWriteStream*, unsigned long) + 94
11 CFNetwork 0x CoreWriteStreamClient::coreStreamEventsAvailable(unsigned long) + 32
12 CFNetwork 0x CoreStreamBase::_callClientNow() + 40
13 CFNetwork 0x3244725c CoreStreamBase::_streamSetEventAndScheduleDelivery(unsigned long, unsigned char) + 84
14 CFNetwork 0x CoreStreamBase::_streamInterface_SignalEvent(unsigned long, CFStreamError const*) + 30
15 CFNetwork 0x323b5c76 SocketStream::socketCallback(__CFSocket*, unsigned long, __CFData const*, void const*) + 130
16 CFNetwork 0x323b5bd6 SocketStream::_SocketCallBack_stream(__CFSocket*, unsigned long, __CFData const*, void const*, void*) + 70
17 CoreFoundation 0x36db5f18 __CFSocketPerformV0 + 792
18 CoreFoundation 0x36db367e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 10
19 CoreFoundation 0x36db2f7a __CFRunLoopDoSources0 + 358
20 CoreFoundation 0x36db1cb2 __CFRunLoopRun + 642
21 CoreFoundation 0x36d24eb8 CFRunLoopRunSpecific + 352
22 CoreFoundation 0x36d839b6 CFRunLoopRun + 94
23 MttHD 0x -[MTTCommandTunnel tunnelThreadMain:] (MTTCommandTunnel.m:113)
24 Foundation 0x32b28678 __NSThread__main__ + 968
25 libsystem_c.dylib 0x337da30c _pthread_start + 304
26 libsystem_c.dylib 0x337da1d4 thread_start + 4
挂的原因是NSRange越界了。
NSInteger length = [self.outputStream write:self.sendBuffer.bytes maxLength:self.sendBuffer.length];
self.sendBuffer = [self.sendBuffer subdataWithRange:NSMakeRange(length, self.sendBuffer.length - length)].mutableC
前一句代码的写动作出错了,返回-1,由于NSRange为无符号型的,最后就发生溢出,成为一个超大整数发生越界。
对于会抛出异常的代码,尤其是数组,字符串类操作比较多的代码,最后try catch 一下。再通过和MTTEXCEPTIONLOG()在catch中输出exception信息。
我添加了两个新的log宏定义MTTERRORLOG()和MTTEXCEPTIONLOG(),这两个宏在调试的时候会很显眼的输出错误信息,并且会将程序主动crash。
便于发现问题。发布版本不会收到影响。
14、initStringWithCString crash bilsonzhou 3.2引入
0 libsystem_c.dylib 0x31e43884 strlen + 12
1 CoreFoundation 0x329e4fa8 CFStringCreateWithCString + 12
2 MttHD 0x0022426e -[ShareView addFavoriteRequest:withPageURL:shareEntrance:] (ShareView.m:349)
3 MttHD 0x00223ac4 -[ShareView addFavoritePage] (ShareView.m:239)
4 MttHD 0x00223a64 -[ShareView canAddFavoritePage] (ShareView.m:231)
5 MttHD 0x -[ShareView handleTouchUp:] (ShareView.m:118)
6 CoreFoundation 0x329e83f6 -[NSObject performSelector:withObject:withObject:] + 46
7 UIKit 0x34fb0e00 -[UIApplication sendAction:to:from:forEvent:] + 56
8 UIKit 0x34fb0dbc -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 24
9 UIKit 0x34fb0d9a -[UIControl sendAction:to:forEvent:] + 38
10 UIKit 0x34fb0b0a -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 486
11 UIKit 0x34fb1442 -[UIControl touchesEnded:withEvent:] + 470
12 UIKit 0x34faf924 -[UIWindow _sendTouchesForEvent:] + 312
13 UIKit 0x34faf312 -[UIWindow sendEvent:] + 374
14 DisplayRecorder.dylib 0x00bb5998 0xba9000 + 51608
15 UIKit 0x34f9568e -[UIApplication sendEvent:] + 350
16 UIKit 0x34f94f34 _UIApplicationHandleEvent + 5820
17 GraphicsServices 0x32b32224 PurpleEventCallback + 876
18 CoreFoundation 0x32a6251c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
19 CoreFoundation 0x32a624be __CFRunLoopDoSource1 + 134
20 CoreFoundation 0x32a6130c __CFRunLoopRun + 1364
21 CoreFoundation 0x329e449e CFRunLoopRunSpecific + 294
22 CoreFoundation 0x329e4366 CFRunLoopRunInMode + 98
23 GraphicsServices 0x32b31432 GSEventRunModal + 130
24 UIKit 0x34fc3cce UIApplicationMain + 1074
25 MttHD 0x main (main.m:15)
26 MttHD 0x 0x1000 + 9904
通常NSString的函数如果传入nil对象是会抛出异常的,但是initStringWithCString这个方法传入nil对象是badexec错误。再调用的时候要记得判断。
15 数据库的单列进行多线程访问 crash bilsonzhou 3.0以前引入
0 libsqlite3.dylib 0x3cx390a3524 (in libsqlite3.dylib) 5212
1 libsqlite3.dylib 0x3c31faae 0x390a1aae (in libsqlite3.dylib) 326
2 libsqlite3.dylib 0x3cx (in libsqlite3.dylib) 6288
3 libsqlite3.dylib 0x3cx (in libsqlite3.dylib) 5776
4 libsqlite3.dylib 0x3cx (in libsqlite3.dylib) 244
5 libsqlite3.dylib 0x3cx (in libsqlite3.dylib) 300
6 libsqlite3.dylib 0x3c303c0e 0x39085c0e (in libsqlite3.dylib) 514
7 libsqlite3.dylib 0x3c38596e (in libsqlite3.dylib) 262
8 libsqlite3.dylib 0x3c33a956 sqlite3_prepare_v2 (in libsqlite3.dylib) 30
9 MttHD 0x0020de14 -[FMDatabase executeQuery:withArgumentsInArray:orVAList:] (in MttHD) (FMDatabase.m:327)
10 MttHD 0x0020dfd8 -[FMDatabase executeQuery:] (in MttHD) (FMDatabase.m:432)
11 MttHD 0x0003fc0a -[MttHistoryManager mostVisitRecordByID:] (in MttHD) (MttHistoryManager.m:715)
12 MttHD 0x000410ce -[MttHistoryManager mostVisitStartPageRecord] (in MttHD) (MttHistoryManager.m:1141)
13 MttHD 0x -[QuicklinkViewNew getUnLockItemCount] (in MttHD) (QuicklinkViewNew.m:1483)
14 MttHD 0x -[MttHDStatInfo protocol] (in MttHD) (MttHDStatInfo.m:179)
15 MttHD 0x [MttSTStat(Adapter) mtthdInstance] (in MttHD) (MttSTStat Adapter.m:39)
16 MttHD 0x00122bec __52-[MttStatService statWithCompleteBlock:failedBlock:]_block_invoke_06 (in MttHD) (MttStatService.m:38)
17 libdispatch.dylib 0x3c52111e _dispatch_call_block_and_release (in libdispatch.dylib) 10
18 libdispatch.dylib 0x3c524ece _dispatch_queue_drain$VARIANT$mp (in libdispatch.dylib) 142
19 libdispatch.dylib 0x3c524dc0 _dispatch_queue_invoke$VARIANT$mp (in libdispatch.dylib) 40
20 libdispatch.dylib 0x3c52591c _dispatch_root_queue_drain (in libdispatch.dylib) 184
21 libdispatch.dylib 0x3c525ac0 _dispatch_worker_thread2 (in libdispatch.dylib) 84
22 libsystem_c.dylib 0x3c555a10 _pthread_wqthread (in libsystem_c.dylib) 360
多个线程都同时对数据库进行访问,造成冲突之后crash。
很多使用单例的地方,如果不做好同步,都有造成线程冲突crash的风险。
解决方法:由于子线程访问数据库的地方不多,将访问数据库都同步到了主线程。
16 进度条绘制和内核的图片绘制冲突 bilsonzhou 3.0以前引入
Thread 0 Crashed:
0 CoreGraphics 0x DYLD-STUB$$ceilf + 0
1 CoreGraphics 0x3723cc86 CGRectIntegral + 86
2 CoreGraphics 0x3724bb82 CGImageBlockCreate + 106
3 ImageIO 0x3349fc70 copyImageBlockSetPNG + 1820
4 ImageIO 0x3349f35e ImageProviderCopyImageBlockSetCallback + 202
5 CoreGraphics 0x3724baf6 CGImageProviderCopyImageBlockSetWithOptions + 226
6 CoreGraphics 0x3724ba00 CGImageProviderCopyImageBlockSet + 32
7 CoreGraphics 0x3724b72e img_blocks_create + 182
8 CoreGraphics 0x img_data_lock + 1656
9 CoreGraphics 0x CGSImageDataLock + 104
10 libRIP.A.dylib 0x3477f63c ripc_AcquireImage + 2676
11 libRIP.A.dylib 0x3477cc52 ripc_DrawImage + 462
12 CoreGraphics 0x CGContextDelegateDrawImage + 44
13 CoreGraphics 0x CGContextDrawImage + 250
14 MttHD 0x -[AJProgressLayer drawInContext:] (UIMttBrowserAddressField.m:89)
15 QuartzCore 0x34cbdd24 _ZL16backing_callbackP9CGContextPv + 32
16 QuartzCore 0x34cbd776 CABackingStoreUpdate + 1226
17 QuartzCore 0x34cbd178 -[CALayer _display] + 724
18 QuartzCore 0x34cbce86 -[CALayer display] + 134
19 QuartzCore 0x34cb1706 CALayerDisplayIfNeeded + 178
20 QuartzCore 0x34cb11c6 CA::Context::commit_transaction(CA::Transaction*) + 214
21 QuartzCore 0x34cb0fd0 CA::Transaction::commit() + 184
22 QuartzCore 0x34caa04e CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 50
23 CoreFoundation 0x36f72a2e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 10
24 CoreFoundation 0x36f7445e __CFRunLoopDoObservers + 406
25 CoreFoundation 0x36f75754 __CFRunLoopRun + 848
26 CoreFoundation 0x36f05ebc CFRunLoopRunSpecific + 224
27 CoreFoundation 0x36f05dc4 CFRunLoopRunInMode + 52
28 GraphicsServices 0x GSEventRunModal + 108
29 GraphicsServices 0x GSEventRun + 56
30 UIKit 0x30efdd62 -[UIApplication _run] + 398
31 UIKit 0x30efb800 UIApplicationMain + 664
32 MttHD 0x main (main.m:15)
33 MttHD 0xx1000 + 9512
mttf偶尔会有该栈的上报,老俞在访问百度贴吧很多图片的时候产生了该crash,在内核线程中也发现了drawInContext函数。
猜测CGContextDrawImage绘制图片和内核绘制图片产生了线程冲突。通过img的drawInRect函数重写了进度条的绘制进行了规避。
17 UIImageWriteToSavedPhotosAlbum 会产生异常 3.2以前引入
0 CoreFoundation 0x341ae8be __exceptionPreprocess (in CoreFoundation) 162
1 libobjc.A.dylib 0x358ca1e4 objc_exception_throw (in libobjc.A.dylib) 32
2 CoreData 0x33b7064a -[NSPersistentStoreCoordinator setMetadata:forPersistentStore:] (in CoreData) 410
3 PhotoLibraryServices 0x328efbb8 [PLManagedObjectContext(Private) recordVersion:forStore:] (in PhotoLibraryServices) 392
4 PhotoLibraryServices 0x328f0022 [PLManagedObjectContext(Private) configurePersistentStoreCoordinator:] (in PhotoLibraryServices) 1122
5 PhotoLibraryServices 0x328f069a __69 [PLManagedObjectContext(Protected) sharedPersistentStoreCoordinator]_block_invoke_0346 (in PhotoLibraryServices) 178
6 libdispatch.dylib 0x374907ea _dispatch_barrier_sync_f_invoke (in libdispatch.dylib) 22
7 libdispatch.dylib 0x3749065a dispatch_barrier_sync_f$VARIANT$up (in libdispatch.dylib) 62
8 libdispatch.dylib 0x3749028e dispatch_sync_f$VARIANT$up (in libdispatch.dylib) 18
9 libdispatch.dylib 0x dispatch_sync$VARIANT$up (in libdispatch.dylib) 32
10 PhotoLibraryServices 0x328f059a [PLManagedObjectContext(Protected) sharedPersistentStoreCoordinator] (in PhotoLibraryServices) 154
11 PhotoLibraryServices 0x328eed94 -[PLManagedObjectContext initWithConcurrencyType:useSharedPersistentStoreCoordinator:] (in PhotoLibraryServices) 120
12 PhotoLibraryServices 0x328eec72 [PLManagedObjectContext contextForPhotoLibrary:] (in PhotoLibraryServices) 114
13 PhotoLibraryServices 0x328ee894 -[PLPhotoLibrary(Protected) loadDatabase] (in PhotoLibraryServices) 236
14 PhotoLibraryServices 0x328b7184 -[PLPhotoLibrary initWithPath:canTriggerDatabaseUpdate:] (in PhotoLibraryServices) 304
15 PhotoLibraryServices 0x328fe1a4 __42 [PLSharedPhotoLibrary sharedPhotoLibrary]_block_invoke_0 (in PhotoLibraryServices) 60
16 libdispatch.dylib 0x dispatch_once_f$VARIANT$up (in libdispatch.dylib) 42
17 PhotoLibraryServices 0x328fe162 [PLSharedPhotoLibrary sharedPhotoLibrary] (in PhotoLibraryServices) 82
18 PhotoLibraryServices 0x328bf06e __withSavedPhotosAlbumUUID_block_invoke_0 (in PhotoLibraryServices) 50
19 libdispatch.dylib 0x dispatch_once_f$VARIANT$up (in libdispatch.dylib) 42
20 PhotoLibraryServices 0x328be436 withSavedPhotosAlbumUUID (in PhotoLibraryServices) 186
21 PhotoLibraryServices 0x328be378 PLSaveImageToCameraRoll (in PhotoLibraryServices) 76
22 UIKit 0x UIImageWriteToSavedPhotosAlbum (in UIKit) 76
23 MttHD 0x001b0eb0 -[MttImageArticleMainView tapButton:] (in MttHD) (MttImageArticleMainVIew.m:983)
24 CoreFoundation 0x -[NSObject performSelector:withObject:withObject:] (in CoreFoundation) 52
25 UIKit 0x352369ea -[UIApplication sendAction:to:from:forEvent:] (in UIKit) 62
26 UIKit 0x -[UIApplication sendAction:toTarget:fromSender:forEvent:] (in UIKit) 30
27 UIKit 0x -[UIControl sendAction:to:forEvent:] (in UIKit) 44
28 UIKit 0x -[UIControl(Internal) _sendActionsForEvents:withEvent:] (in UIKit) 492
29 UIKit 0x3523702c -[UIControl touchesEnded:withEvent:] (in UIKit) 476
30 UIKit 0x3523550e -[UIWindow _sendTouchesForEvent:] (in UIKit) 318
31 UIKit 0x35234f00 -[UIWindow sendEvent:] (in UIKit) 380
32 UIKit 0x3521b4ec -[UIApplication sendEvent:] (in UIKit) 356
33 UIKit 0x3521ad2c _UIApplicationHandleEvent (in UIKit) 5808
34 GraphicsServices 0x35d45df2 PurpleEventCallback (in GraphicsServices) 882
35 CoreFoundation 0x __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ (in CoreFoundation) 38
36 CoreFoundation 0x __CFRunLoopDoSource1 (in CoreFoundation) 140
37 CoreFoundation 0x __CFRunLoopRun (in CoreFoundation) 1370
38 CoreFoundation 0x341044dc CFRunLoopRunSpecific (in CoreFoundation) 300
39 CoreFoundation 0x CFRunLoopRunInMode (in CoreFoundation) 104
40 GraphicsServices 0x35d44fcc GSEventRunModal (in GraphicsServices) 156
41 UIKit 0x UIApplicationMain (in UIKit) 1090
42 MttHD 0x0000377e main (in MttHD) (main.m:15)
43 MttHD 0x start (in MttHD) 40
UIImageWriteToSavedPhotosAlbum这个函数会抛出异常,在网页图片那块保存到本地我就修改过一次。后来的同学估计不知道,又
没有捕获。看来分析还是很有必要的。
18、performSelector 操作会对对象本身retain一下。ranger bilsonzhou
由于异步操作会对对象本身进行一次retain,如果将异步操作放在delloc中cancel。会造成对象的延迟释放,如果这时候对观察者产生
回调的话就会有crash发生。
A做为B的观察者向下传递,B中的C会回调到A中方法。
传统的A的delloc方法中销毁B,B的delloc方法中销毁C。由于异步操作造成B延迟释放,
A delloc之后,B没有立即放生释放,这时候B产生回调或者C发生回调,就会因为A的野指针crash。
搜索框的crash的根本原因也是这个,下面是crash栈
0 libobjc.A.dylib 0x33a54c98 objc_msgSend + 16
1 MttHD 0x -[UIToolbarSearchInputPopMenuController onGetAssociationOfInput:data:] (UIToolbarSearchInputPopMenuController.m:574)
2 MttHD 0x __block_global_0 (MttGetAssociationalService.m:58)
3 libdispatch.dylib 0x _dispatch_barrier_sync_f_slow_invoke + 62
4 libdispatch.dylib 0x _dispatch_main_queue_callback_4CF$VARIANT$mp + 282
5 CoreFoundation 0x __CFRunLoopRun + 1328
6 CoreFoundation 0x37303ebc CFRunLoopRunSpecific + 224
7 CoreFoundation 0x37303dc4 CFRunLoopRunInMode + 52
8 GraphicsServices 0x31c5c418 GSEventRunModal + 108
9 GraphicsServices 0x31c5c4c4 GSEventRun + 56
10 UIKit 0x312fbd62 -[UIApplication _run] + 398
11 UIKit 0x312f9800 UIApplicationMain + 664
12 MttHD 0x main (main.m:15)
13 MttHD 0x 0x1000 + 9968
- (void)onGetAssociationOfInput:(NSString *)keyWord data:(NSArray *)result
NSString* lastSearchKeyWord = keyW
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(loadSearchRecord) object:nil];
[[MttHDStatInfo theSpecial] statUserBehavior:MttHDUserBehavior_Receive_Associational];
NSString *searchString = self.searchS
这个地方是搜索热词的回调代码,这里面的self在输入弹出框消失的时候会先进行一次释放,
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(loadSearchRecord) object:nil];
这句代码会将异步对自身的retain再释放一次。如果网络情况差,热词回调慢于弹出框释放,上面
的代码的实际执行就会如下:
NSString* lastSearchKeyWord = keyW
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(loadSearchRecord) object:nil];
[[MttHDStatInfo theSpecial] statUserBehavior:MttHDUserBehavior_Receive_Associational];
[self release];
[self delloc];
//这时候self已经野了,这句话以执行就会野指针挂掉
NSString *searchString = self.searchS
这个crash的随机性很高,通常情况时热词先回来,弹出框再消失就不会发生,只有在网络回调慢的时候才会发生。
针对这种情况,需要对有异步操作的函数写一个destroy函数来cancel异步操作,在观察者发生释放的时候调用destroy函数。以达到同步释放
后续这块会对异步操作进行梳理。
19、代理对象被释放,delegate没有设置Nil,delegate回调野指针导致block发生crash. 3.2引入 bilsonzhou
0 libobjc.A.dylib 0x317dff78 objc_msgSend (in libobjc.A.dylib) 16
1 libdispatch.dylib 0x3491fc58 _dispatch_call_block_and_release (in libdispatch.dylib) 12
2 libdispatch.dylib 0x34921ee6 _dispatch_main_queue_callback_4CF$VARIANT$mp (in libdispatch.dylib) 194
3 CoreFoundation 0x377432ac __CFRunLoopRun (in CoreFoundation) 1268
4 CoreFoundation 0x376c64a4 CFRunLoopRunSpecific (in CoreFoundation) 300
5 CoreFoundation 0x376c636c CFRunLoopRunInMode (in CoreFoundation) 104
6 GraphicsServices 0x378bf438 GSEventRunModal (in GraphicsServices) 136
7 UIKit 0x308d2cd4 UIApplicationMain (in UIKit) 1080
8 MttHD 0x00003ace main (in MttHD) (main.m:15)
订阅第二屏subscribItem被释放了,但是wup回调的delegate没有被设置空。
在读取数据的轮询函数中,将
if(imageData == nil)
注释掉了。
之后下面的:
[_subscribeWupManager release];
_sbuscribeWupManager =
代码被调用,在弱网络环境下,wup的回调如果还没有回来,这个时候由于wup里面的block会retain _subscribeWupManager自身。
当 subscribItem delloc的时候调用 _subscribeWupManager.delegate =这时候的_subscribeWupManager已经是一个指向
nil的指针。而真正的_subscribeWupManager此时并没有释放,但是它的delegate已经释放,这时候发生回调就发生Crash了.
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见}

我要回帖

更多关于 delegate 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信