字典中ECClient对象过度释放引发EXC_BAD_ACCESS错误的解决方法
解决ECClient过度释放/僵尸对象崩溃问题
从你的代码和NSZombie崩溃日志来看,核心问题是ECClient对象被释放后仍有代码(大概率是异步回调或block)尝试访问它,或者多持有者的引用计数管理不当导致重复释放风险。下面是针对性的修复方案:
1. 明确对象所有权,避免重复强引用
你的代码里ECClient同时被ECRoom(publishClient属性、p2pClients字典)和ECSignalingChannel(signalingDelegate)持有,需要明确所有权边界:
ECRoom作为ECClient的创建者和生命周期管理者,保持强持有(比如publishClient用strong属性,p2pClients用强引用字典),负责对象的创建和销毁时机。- 将
ECSignalingChannel的signalingDelegate改为弱引用(声明为weak id<ECSignalingChannelDelegate> signalingDelegate;),因为它只需要接收回调,不需要拥有对象所有权。这能避免双强引用导致的引用计数异常,也能防止循环引用。
2. 清理异步回调/block中的引用
崩溃日志显示问题出在ECClient.m:245的block销毁逻辑中,说明ECClient内部的block可能捕获了强引用的self,导致对象释放后block仍在执行,访问僵尸对象。修复方法:
- 在
ECClient的dealloc中主动取消所有未完成的异步任务、WebRTC回调,终止所有可能触发后续访问的操作,比如关闭PeerConnection、移除监听者。 - 使用block时,用弱引用捕获
self,并在block内部先判断对象是否存活:__weak typeof(self) weakSelf = self; someAsyncCallback = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; // 后续操作使用strongSelf访问对象 };
3. 统一管理ECClient的生命周期
当不再需要ECClient时(比如流停止发布、P2P连接断开),需要做完整的清理:
- 从
ECRoom的p2pClients字典中移除对应实例,避免字典强持有导致对象无法释放。 - 通知
ECSignalingChannel清除对应代理引用,防止后续回调触发。 - 显式将
publishClient等属性置为nil,触发ARC的引用计数递减。
示例代码:
// ECRoom.m 停止发布的方法 - (void)stopPublish { [_signalingChannel clearSignalingDelegate:self.publishClient]; self.publishClient = nil; } // ECRoom.m 移除P2P客户端的方法 - (void)removeP2PClientWithPeerSocketId:(NSString *)peerSocketId { ECClient *client = [p2pClients objectForKey:peerSocketId]; [_signalingChannel clearSignalingDelegate:client]; [p2pClients removeObjectForKey:peerSocketId]; }
4. 规范ECClient的dealloc实现
确保ECClient的dealloc方法中彻底清理资源,避免触发异步回调:
// ECClient.m - (void)dealloc { // 关闭WebRTC连接 if (_peerConnection) { [_peerConnection close]; _peerConnection = nil; } // 清空代理,切断回调链 self.delegate = nil; // 取消所有未完成的异步任务 [_pendingOperations cancelAllOperations]; NSLog(@"ECClient dealloc: %p", self); }
5. 跟踪对象生命周期(可选)
如果问题仍未解决,可以在ECClient的init和dealloc中添加日志,跟踪对象的创建和释放时机,确认是否存在创建/释放不匹配的情况:
- (instancetype)initWithDelegate:(id<ECClientDelegate>)delegate ... { self = [super init]; if (self) { NSLog(@"ECClient created: %p", self); // 其他初始化逻辑 } return self; }
通过以上步骤,应该能解决ECClient的过度释放和僵尸对象崩溃问题。
内容的提问来源于stack exchange,提问作者allenlinli
相关产品推荐
相关产品推荐

