iOS调用postUpdateRequest时出现nil对象插入崩溃及聊天认证问题
崩溃根因
报错*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]可以直接定位到postUpdateRequest的参数字典构造环节:
- 你在方法里强制把
_window.rootViewController类型转成UINavigationController,再取栈底控制器作为ChatViewController - 但你现在的层级结构已经换成由UITabBarController作为容器,根控制器根本不是UINavigationController,强转后取到的
chatViewController为nil,顺着引用链拿到的dataModel也为nil - 构造的字典一共3个参数:第一个
@"cmd":@"update"是硬编码非空值,第二个user_id的值因为链路断裂从nil的dataModel返回nil,刚好对应报错里的objects[1](数组索引从0开始,第二个元素就是索引1),完全匹配崩溃场景,你附的崩溃栈截图也能验证:调用栈最终终止在NSDictionary初始化环节,前序调用帧就是postUpdateRequest方法,和定位的原因完全吻合。
架构层面的核心问题
你现在把DataModel和ChatViewController做了强绑定,所有需要访问用户数据、发请求的场景都必须先找到ChatViewController才能拿到数据模型。这种设计在你把UITabBarController引入、流程拆分到4个独立页面后,VC层级一旦发生变化,取数链路就会直接断裂,根本无法稳定支撑后续的POST请求逻辑。
适配当前业务的可行方案
1. 解耦DataModel的归属,改为全局单例
这是最能从根源解决问题的方案:
- 把DataModel改造为单例类,内部封装所有NSUserDefaults的读写逻辑,所有需要读取用户ID、设备token、昵称、加入聊天状态的场景,直接通过
[DataModel sharedModel]访问,完全不需要绕到ChatViewController取实例 - 可以保留ChatViewController里的dataModel属性,但改为从单例引用,不改变原有页面读取数据的代码逻辑
- 单例的生命周期和应用一致,不管用户走到哪个流程节点、TabBar怎么切换,只要需要发请求都能稳定拿到非空的数据源,不会因为VC栈的变化导致nil值
2. 保留原有绑定逻辑的容错改造
如果不想调整DataModel的现有结构,至少要修正层级取值的容错逻辑:
- 获取根控制器后先做类型判断,不直接强转:如果根控制器是UITabBarController,遍历所有子控制器,找到承载ChatViewController的UINavigationController,再从导航栈中取出ChatViewController实例
- 每一步对象获取后都做空值校验,拿到的chatViewController、dataModel都判空,绝对不要给nil对象发消息导致链路断裂
- 构造请求参数时,所有从dataModel取出的值都做兜底处理,哪怕拿到nil也替换为空字符串,禁止向字典插入nil值
3. 登录流程的衔接优化
你现在把登录流程从ChatVC的模态弹窗移到了TabBar流程的节点,需要做好状态同步:
- 登录接口调用成功、服务端返回允许加入聊天后,立刻把
joinedChat状态写入DataModel,不要等用户回到ChatVC页面才做状态校验 - 设备token注册成功的回调触发时,只要用户状态为已加入,直接通过数据源拿到参数发起更新请求,不依赖ChatVC的存在
细节排查修正点
- 把
LoginViewController中dataModel的属性修饰符从assign改为strong,避免DataModel被释放后出现野指针引用 - 原有userId的获取逻辑(空值时生成UUID并写入本地)是没问题的,不需要调整,崩溃的核心是之前根本没走到这个get方法
内容的提问来源于stack exchange,提问作者programcode
相关产品推荐
相关产品推荐

