React Native iOS发布模式点击按钮崩溃,调试模式正常求助
问题排查与修复方案
我一眼就注意到崩溃日志里的关键错误:-[__NSCFNumber length]: unrecognized selector sent to instance——这是典型的把数字类型对象当成字符串调用length方法导致的崩溃,而问题的核心出在你对AsyncStorage的使用上:
React Native的AsyncStorage只支持存储字符串类型的数据,调试模式下RN会帮你做一些隐式的类型转换(比如自动把数字转成字符串),但发布模式下这些调试期的“容错处理”会被优化掉,所以就触发了原生层的异常。
具体问题点
看你代码里的这段逻辑:
}else{ console.log('if no deleted projects'); await AsyncStorage.setItem('@TOS:deletedProjects', project.p_id); }
这里你直接把project.p_id(明显是数字类型)存进了AsyncStorage,没有转成字符串。发布模式下,这个数字会被原生层处理成NSNumber对象,之后当你读取它并尝试做字符串拼接(比如'"' + deletedProjects + '"')时,原生代码会试图调用length方法来处理这个NSNumber,自然就抛出了“无法识别的选择器”异常。
另外,你手动拼接字符串来维护已删除项目ID的方式也很容易出错,比如如果之前存储的格式不规范,后续解析或拼接都会出问题,更稳妥的方式是用数组来管理这些ID,再转成JSON字符串存储。
修复后的代码
// 读取项目列表时,处理getItem返回null的情况,避免JSON.parse报错 let projects = JSON.parse(await AsyncStorage.getItem('@TOS:projects') || '[]'); console.log('projects', projects); let index = projects.findIndex(x => x.p_id === project.p_id); console.log(projects, index); // 先判断index是否有效,防止splice操作报错 if (index !== -1) { projects.splice(index, 1); console.log('projects', projects); await AsyncStorage.setItem('@TOS:projects', JSON.stringify(projects)); } // 重构已删除项目的处理逻辑,用数组管理ID let deletedProjects = await AsyncStorage.getItem('@TOS:deletedProjects'); let deletedIds = []; if (deletedProjects != null) { try { deletedIds = JSON.parse(deletedProjects); // 确保解析后是数组,避免旧数据格式导致的问题 if (!Array.isArray(deletedIds)) { deletedIds = []; } } catch (e) { // 解析失败时重置为空数组 deletedIds = []; console.warn('Failed to parse deleted projects, resetting to empty array', e); } } // 避免重复添加同一个ID if (!deletedIds.includes(project.p_id)) { deletedIds.push(project.p_id); } // 统一转成JSON字符串存储,保证类型一致性 await AsyncStorage.setItem('@TOS:deletedProjects', JSON.stringify(deletedIds)); console.log('in success');
关键注意事项
- 强制字符串存储:所有存入
AsyncStorage的数据必须是字符串类型,数字、数组、对象都要通过JSON.stringify转换后再存储。 - 处理读取异常:读取
AsyncStorage时要考虑返回null的情况,以及JSON.parse失败的场景,做好兜底处理。 - 避免手动拼接字符串:维护列表类数据时,用数组+JSON序列化的方式比手动拼接字符串可靠得多,能避免格式混乱和类型错误。
内容的提问来源于stack exchange,提问作者Waleed Naveed
相关产品推荐
相关产品推荐

