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

NSSecureCoding实现问题:解码TextDocumentClass数组返回nil求助

NSSecureCoding解码TextDocumentClass数组返回nil的问题

实现NSSecureCoding时,解码后无法获取TextDocumentClass类型数组,返回值始终为nil。尝试过在aDecoder.decodeObject中传入[TextDocumentClass.self]、NSArray.self等参数均无效,同时自定义了MyClassValueTransformer以支持默认NSSecureUnarchiveFromDataTransformer未包含的类,相关代码及错误日志如下:

WrapperClass代码

public class WrapperClass:NSObject, NSCoding, NSSecureCoding {
    public static var supportsSecureCoding: Bool = true
    
    var attributedStrings = [TextDocumentClass]()
    
    init(pAttributedStrings:[TextDocumentClass]) {
        self.attributedStrings = pAttributedStrings
    }
    
    required public init?(coder aDecoder: NSCoder){
        self.attributedStrings = aDecoder.decodeObject(of: [TextDocumentClass.self], forKey: "attributedStrings") as? [TextDocumentClass]
        
        //return self
    }
    
    public func encode(with aCoder: NSCoder) {
        aCoder.encode(attributedStrings,forKey:"attributedStrings")
    }
}

自定义ValueTransformer代码

@objc(MyTestClassValueTransformer)
final class MyClassValueTransformer: NSSecureUnarchiveFromDataTransformer {

    static let name = NSValueTransformerName(rawValue: String(describing: MyClassValueTransformer.self))
    
    public static func register() {
        let transformer = MyClassValueTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }
    
    override static var allowedTopLevelClasses: [AnyClass] {
        return super.allowedTopLevelClasses + [WrapperClass.self, UIFont.self, NSObject.self, NSParagraphStyle.self]
    }
    
    override public class func transformedValueClass() -> AnyClass {
            return WrapperClass.self
        }

        override public class func allowsReverseTransformation() -> Bool {
            return true
        }
    
    override public func transformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else {
            return nil
        }

        do {
            let box = try NSKeyedUnarchiver.unarchivedObject(
                ofClass: WrapperClass.self,
                from: data
            )
            return box
        } catch {
            return nil
        }
    }
    
    override public func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let box = value as? WrapperClass else {
            return nil
        }

        do {
            let data = try NSKeyedArchiver.archivedData(
              withRootObject: box,
              requiringSecureCoding: true
          )
            return data
        } catch {
            return nil
        }
    }
}

TextDocumentClass代码

public class TextDocumentClass:NSObject, NSCoding, NSSecureCoding {
    public static var supportsSecureCoding: Bool = true
    
    var attributedText: NSAttributedString!
    var uuid:String!
    var isDeleted:Bool!
    var isChanged:Bool!
    var prevPageUUID:String!
    var nextPageUUID:String!
    var order:Int!
    var lastUpdatedDate:String!
    var isPDF: Bool!
    var pdfURL: String!
    var pdfPageNumber: Int!
    
    init (pAttributedText:NSAttributedString, pUUID:String, pPrevPageUUID:String, pNextPageUID:String, pOrder:Int, pIsDeleted: Bool, pIsChanged: Bool, pLastUpdatedDate: String, isPDF: Bool = false, pdfURL: String = "", pdfPageNumber: Int = -1) {
        self.attributedText = pAttributedText
        self.uuid = pUUID
        self.isDeleted = pIsDeleted
        self.isChanged = pIsChanged
        self.prevPageUUID = pPrevPageUUID
        self.nextPageUUID = pNextPageUID
        self.order = pOrder
        self.lastUpdatedDate = pLastUpdatedDate
        self.isPDF = isPDF
        self.pdfURL = pdfURL
        self.pdfPageNumber = pdfPageNumber
    }
    
    required public init?(coder aDecoder: NSCoder){
        self.attributedText = (aDecoder.decodeObject(of: NSAttributedString.self, forKey: "attributedText"))! 
        self.prevPageUUID = (aDecoder.decodeObject(of: NSString.self, forKey: "prevPageUUID") as? String)! 
        self.nextPageUUID = (aDecoder.decodeObject(of: NSString.self, forKey: "nextPageUUID") as? String)! 
        self.uuid = (aDecoder.decodeObject(of: NSString.self, forKey: "uuid") as? String)! 
        self.isChanged = (aDecoder.decodeObject(of: NSNumber.self, forKey: "isChanged")?.boolValue)! 
        self.isDeleted = (aDecoder.decodeObject(of: NSNumber.self, forKey: "isDeleted")?.boolValue)! 
        self.order = (aDecoder.decodeObject(of: NSNumber.self, forKey: "order")?.intValue)! 
        self.lastUpdatedDate = (aDecoder.decodeObject(of: NSString.self, forKey: "lastUpdatedDate") as? String)! 
        self.isPDF = aDecoder.decodeObject(of: NSNumber.self, forKey: "isPDF")?.boolValue ?? false 
        self.pdfURL = (aDecoder.decodeObject(of: NSString.self, forKey: "pdfURL") as? String ?? "") 
        self.pdfPageNumber = aDecoder.decodeObject(of: NSNumber.self, forKey: "PDFpageNumber")?.intValue ?? -1 
    }
    
    public func encode(with aCoder: NSCoder) {
        aCoder.encode(attributedText,forKey:"attributedText")
        aCoder.encode(prevPageUUID,forKey:"prevPageUUID")
        aCoder.encode(nextPageUUID,forKey:"nextPageUUID")
        aCoder.encode(uuid,forKey:"uuid")
        aCoder.encode(isChanged,forKey:"isChanged")
        aCoder.encode(isDeleted,forKey:"isDeleted")
        aCoder.encode(order,forKey:"order")
        aCoder.encode(lastUpdatedDate,forKey:"lastUpdatedDate")
        aCoder.encode(isPDF, forKey: "isPDF")
        aCoder.encode(pdfURL, forKey: "pdfURL")
        aCoder.encode(pdfPageNumber, forKey: "PDFpageNumber")
    }
    
    override public init() {
        self.attributedText = NSAttributedString()
        self.uuid = ""
        self.isDeleted = false
        self.isChanged = false
        self.prevPageUUID = ""
        self.nextPageUUID = ""
        self.order = -1
        self.lastUpdatedDate = ""
        self.isPDF = false
        self.pdfURL = ""
        self.pdfPageNumber = -1
    }
}

错误日志

//此代码行
let temp = try aDecoder.decodeTopLevelObject(of: [BaselineClass.self], forKey: "attributedStrings")
//控制台输出如下
Allowed classes are:
 {(
    "'demo.BaselineClass' (0x100f6fc40) [/private/var/containers/Bundle/Application/EF7BF8EB-A98B-4B52-AE8F-1FB05F31ECC3/demo.app]"
)}" UserInfo={NSDebugDescription=value for key 'attributedStrings' was of unexpected class 'NSArray' (0x1fc4b89e0) [/System/Library/Frameworks/CoreFoundation.framework].
Allowed classes are:
 {(
    "'demo.BaselineClass' (0x100f6fc40) [/private/var/containers/Bundle/Application/EF7BF8EB-A98B-4B52-AE8F-1FB05F31ECC3/demo.app]"
)}}
//若修改为如下代码
let temp = try aDecoder.decodeTopLevelObject(of: NSArray.self, forKey: "attributedStrings")
//控制台输出变为
Allowed classes are:
 {(
    "'NSArray' (0x1fc4b89e0) [/System/Library/Frameworks/CoreFoundation.framework]"
)}" UserInfo={NSDebugDescription=value for key 'NS.objects' was of unexpected class 'demo.BaselineClass' (0x103363c40) [/private/var/containers/Bundle/Application/1E080145-14D1-4D03-9520-E87C39B8F2C7/demo.app].
Allowed classes are:
 {(
    "'NSArray' (0x1fc4b89e0) [/System/Library/Frameworks/CoreFoundation.framework]"
)}}

解决方案

  • 修正数组解码的允许类:在WrapperClass的init?(coder:)方法中,解码数组时需要同时指定数组类和元素类,因为存储的是NSArray类型的数组,元素是TextDocumentClass,所以修改解码代码为:
    self.attributedStrings = aDecoder.decodeObject(of: [NSArray.self, TextDocumentClass.self], forKey: "attributedStrings") as? [TextDocumentClass]
    
  • 更新ValueTransformer的允许类:在MyClassValueTransformer的allowedTopLevelClasses中添加TextDocumentClass.self,确保嵌套的元素类被允许解码:
    override static var allowedTopLevelClasses: [AnyClass] {
        return super.allowedTopLevelClasses + [WrapperClass.self, TextDocumentClass.self, UIFont.self, NSParagraphStyle.self]
    }
    
  • 避免强制解包:TextDocumentClass的解码方法中存在大量强制解包(!),一旦解码失败会直接崩溃,建议替换为可选绑定或默认值,例如:
    if let attributedText = aDecoder.decodeObject(of: NSAttributedString.self, forKey: "attributedText") {
        self.attributedText = attributedText
    } else {
        self.attributedText = NSAttributedString()
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:09:57