最近这段时间遇到了两次比较严重的真机崩溃问题, 都是之前所没有遇到过的, 特此记录一下, 希望能帮助到遇到类似问题的朋友.
之所以强调真机, 是因为这些问题在 player 上或者 debug 版无法出现, 只有真正运行在手机上才可能遇到, 因为最后我们 Archive 出来的包都是 release 版的.
1. removeFromParent
removeFromParent() 多次会导致崩溃并闪退, 很难以想象是吧, 在我的记忆中应该只是抛出一个 lua 错误. 事实上, debug 版确实如此, 让我们看一段测试代码:
1 | self._btnLogin:removeFromParent() |
player 上会收到如下的 lua 错误提示:
1 | LUA ERROR: [string "xxx.lua"]:349: invalid 'cobj' in function 'lua_cocos2dx_Node_removeFromParentAndCleanup' |
让我们看下抛出这个错误的地方 lua_cocos2dx_Node_removeFromParentAndCleanup
:
1 |
|
第一次调用完 removeFromParent(), _btnLogin
的 c++ 对象就已经被释放了, 再次调用 cobj
就是 NULL
了, 所以就会进入到这个判断中, 从而抛出错误.
但是这段代码却是运行在 COCOS2D_DEBUG 宏中的, 就意味着这段代码在 release 版是不生效的, 所以就会接着往下执行, 从而导致崩溃.
虽然我们不太可能直接会用一个对象调用 removeFromParent 多次, 但是包含 removeFromParent 的函数被调用多次却是很有可能的, 所以我们一定要养成一个好的习惯, 在 removeFromParent
之前要做判空检测, 之后要立刻置空:
1 | if xxx then |
2. Signal 13 was raised. SIGPIPE
当游戏在 iOS 上不切换到后台直接锁屏一段时间, 网络资源就会被系统回收掉, 这时候解锁屏幕, 游戏并不知道 tcp 连接已经断开, 发送消息就会触发这个崩溃.
表现出来的效果是手机解锁不了, 得等好久才可以.
关于具体技术解释, 大家可以看这篇文章, Signal 13 was raised(SIGPIPE管道破裂), 我这里重点讲解决方案.
网上说有两种解决方案(如何在 iOS 上避免 SIGPIPE 信号导致的 crash):
1. 在全局范围内忽略这个信号
1 | signal(SIGPIPE, SIG_IGN); |
2. 在一开始的时候设置 socket 不要发送 SIGPIPE 信号
1 | int value = 1; |
就 1 来说, cocos 早已设置了忽略, 在 socket_open
函数中, 并且这的执行到了. 我在 tcp.c
中的 tcp_create
中加入 2 方案, 确实解决了这个问题. 但是 SO_NOSIGPIPE
并不是跨平台的, 我偷懒直接用宏判断了下:
1 | static int tcp_create(lua_State *L, int family) { |