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

NSCoder编码解码[Data]数组报错,未适配NSSecureCoding求助

问题描述

在使用NSCoder协议对[Data]数组(本质为文件书签)进行编码/解码时遇到问题,以下是我的实现代码:

class ClosedFiles: NSCoder {
    static var supportsSecureCoding = true
    let bookmarks: [Data]
    init?(with coder: NSCoder) {
        if let bm = coder.decodeObject() as? [Data] {
            bookmarks = bm
        } else {
            return nil
        }
    }
    init(bookmarks: [Data]) {
        self.bookmarks = bookmarks
    }
    
    func encode(with coder: NSCoder) {
        coder.encodeRootObject(bookmarks)
    }
}

在AppDelegate中对该对象进行编码:

func application(_ app: NSApplication, willEncodeRestorableState coder: NSCoder) {
    coder.encodeRootObject(ClosedFiles(bookmarks: closedFiles))
}

运行时抛出如下错误:

Ignoring exception: This decoder will only decode classes that adopt NSSecureCoding. Class '_TtCC7MyApp11AppDelegate11ClosedFiles' does not adopt it.

我按照Apple官方文档适配NSSecureCoding,但似乎不起作用,尝试了coder.encode()、coder.encodeConditionalObject等其他编码方法也无效,请问我哪里出错了?


问题分析与解决

你的代码存在几个关键错误,导致无法正确适配NSSecureCoding:

  • 类的继承与协议实现错误
    你让ClosedFiles继承了NSCoder,但实际上需要继承NSObject并遵守NSSecureCoding协议。NSCoder是编码解码的工具类,不是需要编码的对象应该继承的父类。

  • 初始化方法签名错误
    实现NSSecureCoding的解码方法必须是init?(coder: NSCoder),而不是init?(with coder: NSCoder),签名不匹配会导致系统无法识别你实现了安全解码逻辑。

  • 编码解码方法不符合安全规范
    编码时应该使用encode(_:forKey:)而不是encodeRootObject(_:),后者是旧的非安全编码方式,不支持安全校验;解码时必须用带类型校验的decodeObject(of:forKey:),不能用无参数的decodeObject(),这是安全编码的核心要求。

  • supportsSecureCoding未被正确识别
    虽然你声明了static var supportsSecureCoding = true,但由于前面的错误,系统并没有正确识别你的类支持安全编码。


修正后的代码

ClosedFiles类实现

import Foundation

class ClosedFiles: NSObject, NSSecureCoding {
    static var supportsSecureCoding: Bool = true
    
    let bookmarks: [Data]
    
    // 自定义初始化方法
    init(bookmarks: [Data]) {
        self.bookmarks = bookmarks
        super.init()
    }
    
    // 解码方法(必须匹配init?(coder:)签名)
    required init?(coder: NSCoder) {
        // 安全解码:指定数组和元素类型,对应Objective-C中的NSArray<NSData*>
        guard let bookmarks = coder.decodeObject(of: [NSArray.self, NSData.self], forKey: "bookmarks") as? [Data] else {
            return nil
        }
        self.bookmarks = bookmarks
        super.init()
    }
    
    // 编码方法
    func encode(with coder: NSCoder) {
        // 使用带key的编码方法,和解码时的key对应
        coder.encode(bookmarks, forKey: "bookmarks")
    }
}

AppDelegate中的编码调用

func application(_ app: NSApplication, willEncodeRestorableState coder: NSCoder) {
    // 用encode(_:forKey:)替代encodeRootObject(_:)
    coder.encode(ClosedFiles(bookmarks: closedFiles), forKey: "closedFiles")
}

额外说明

  • 解码时decodeObject(of:forKey:)的类型参数需要同时传入数组类型NSArray.self和元素类型NSData.self,因为Swift的[Data]在Objective-C层对应NSArray<NSData *>。
  • 编码和解码必须使用相同的key,确保数据能正确映射。
  • 确认closedFiles确实是[Data]类型,避免类型不匹配导致解码失败。

内容的提问来源于stack exchange,提问作者Soid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:07:17