objc_object::release()崩溃求助:附调用栈,无法复现求原因分析
首先还原你的崩溃调用栈:
Crashed: com.apple.NSURLSession-work 0 libobjc.A.dylib 0x1829d17f4 objc_object::release() + 16 1 libsystem_blocks.dylib 0x18318ca5c _Block_release + 152 2 libdispatch.dylib 0x1830ecae4 _dispatch_client_callout + 16 3 libdispatch.dylib 0x1831297a8 _dispatch_continuation_pop$VARIANT$armv81 + 416 4 libdispatch.dylib 0x183132acc _dispatch_source_invoke$VARIANT$armv81 + 908 5 libdispatch.dylib 0x18312b074 _dispatch_queue_serial_drain$VARIANT$armv81 + 248 6 libdispatch.dylib 0x18312bad8 _dispatch_queue_invoke$VARIANT$armv81 + 328 7 libdispatch.dylib 0x18312c47c _dispatch_root_queue_drain_deferred_wlh$VARIANT$armv81 + 332 8 libdispatch.dylib 0x18313444c _dispatch_workloop_worker_thread$VARIANT$armv81 + 612 9 libsystem_pthread.dylib 0x18341fe70 _pthread_wqthread + 860 10 libsystem_pthread.dylib 0x18341fb08 start_wqthread + 4
从调用栈能看出,崩溃发生在objc_object::release(),且是在释放Block(_Block_release)的流程中触发的,整个上下文是NSURLSession的后台工作线程。结合iOS内存管理的常见问题,这里有几个最可能的诱因:
Block捕获了已被释放的对象(野指针访问)
当你用NSURLSession发起网络请求时,回调Block(比如completionHandler)会自动捕获外部对象。如果这个对象在请求完成前就被释放了(比如持有它的ViewController被pop、或者没有强引用维持它的生命周期),当Block后续执行到释放该对象的步骤时,就会访问一个已经被回收的内存地址,触发崩溃。这种情况在异步请求场景里特别常见,因为请求的完成时机是不确定的。Block自身的内存管理异常
如果你手动对Block进行了retain/release操作(而非完全依赖ARC自动管理),很可能会导致Block的引用计数混乱。当系统尝试释放Block时,其内部的结构已经损坏,进而在执行_Block_release时触发objc_object::release()的崩溃。NSURLSession生命周期与请求回调不匹配
如果你在还有未完成请求的情况下销毁了NSURLSession实例,那些待执行的请求回调Block仍然会留在dispatch队列中等待处理。当这些Block最终执行时,它们引用的NSURLSession相关对象已经被释放,同样会引发内存访问错误。多线程竞态条件导致的内存冲突
比如在主线程中释放了某个对象,而NSURLSession的工作线程此时正通过Block尝试访问或释放该对象,这种跨线程的内存操作冲突也会触发这类崩溃。
给你几个排查的方向:
- 开启Xcode的Zombie Objects调试功能,它能在你访问已释放对象时给出精准的对象信息,帮你快速定位是哪个对象出了问题。
- 检查所有NSURLSession请求的回调Block,确保捕获的对象有合理的内存管理策略:比如用
weak self+ 内部转strong self的方式避免循环引用,同时保证对象在Block执行期间不会被提前释放;如果是可选对象,在使用前一定要做nil检查。 - 规范NSURLSession的生命周期:要么确保所有请求完成后再销毁Session,要么在销毁时调用
invalidateAndCancel()主动取消所有未完成的请求,阻止回调Block继续执行。 - 检查代码中是否有手动管理Block内存的逻辑,尽量完全依赖ARC来处理Block的引用计数。
内容的提问来源于stack exchange,提问作者Oreex0

