You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 14:45:08