Swift中NSKeyedUnarchiver抛NSInvalidArgumentException崩溃且do-catch无法捕获
1. 为什么do-catch没有捕获到异常
Swift的do-catch机制只能捕获Swift标准Error类型,以及Objective-C桥接过来的NSError。你遇到的NSInvalidArgumentException属于Objective-C的Runtime异常(NSException),这类异常默认无法被Swift的do-catch捕获,只能通过Objective-C的@try-@catch机制拦截。
2. 崩溃的直接原因
错误日志-[__NSCFData count]: unrecognized selector说明:解档时预期得到[CLLocation]数组,但实际从归档数据里解出来的是NSData类型对象,系统尝试对这个Data对象调用数组的count方法,触发了无法识别选择器的崩溃。
这种偶现错误通常是归档阶段的多线程问题导致:track是NSManagedObject类型,必须在其所属的NSManagedObjectContext对应的线程操作,如果你在其他线程直接写入trackLocation属性,很容易出现数据写入异常,把错误的Data内容存了进去。
方案1:添加NSException拦截层
新增一个Objective-C的工具类,把可能抛出异常的解档逻辑包裹在@try-@catch中,将异常转换为Swift可处理的Error返回:
// NSKeyedUnarchiver+ExceptionCatch.h #import <Foundation/Foundation.h> @interface NSKeyedUnarchiver (ExceptionCatch) + (nullable NSArray *)unarchiveArrayOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error; @end
// NSKeyedUnarchiver+ExceptionCatch.m #import "NSKeyedUnarchiver+ExceptionCatch.h" @implementation NSKeyedUnarchiver (ExceptionCatch) + (nullable NSArray *)unarchiveArrayOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error { @try { NSArray *result = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:cls fromData:data error:error]; return result; } @catch (NSException *exception) { if (error) { *error = [NSError errorWithDomain:exception.name code:-1 userInfo:exception.userInfo]; } return nil; } } @end
在桥接Header中引入该头文件后,就可以在Swift中安全调用,异常会被转为Error进入catch块。
方案2:修复归档阶段的多线程问题
所有对track属性的读写操作都要放到其所属NSManagedObjectContext的perform/performAndWait块中执行,避免跨线程操作Core Data对象导致的数据错乱。
方案3:增加解档前的类型校验
如果不想引入Objective-C桥接代码,也可以先解档为通用对象,先做类型判断再转换:
do { let out = try Data(referencing: NSData(data: track.trackLocation).decompressed(using: .lzma)) let archiver = try NSKeyedUnarchiver(forReadingFrom: out) archiver.requiresSecureCoding = true archiver.decodingFailurePolicy = .setErrorAndReturn // 先解档为Any,判断类型后再处理 guard let rawObj = archiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) else { return } guard let locationArray = rawObj as? [CLLocation] else { print("解档类型错误,预期[CLLocation],实际得到\(type(of: rawObj))") return } track.decodedTrackLocation = locationArray } catch { print(error) }
内容的提问来源于stack exchange,提问作者Matter4478

