英文:
NSKeyedUnarchiver unarchiveTopLevelObjectWithData deprecated - Array loads as nil even though it's in the archive
问题
自macOS 14.0开始,NSKeyedUnarchiver.unarchiveTopLevelObjectWithData
已被弃用,我现在尝试使用在类中设置了NSSecureCoding并使用以下方法加载遗留的NSCoder存档:
coder.unarchivedObject(ofClasses classes: [AnyClass], from data: Data)
来加载存档。存档可以加载,大多数类成员可以提取。但是,Swift数组始终返回nil,即使coder.containsValue(forKey:)
返回true。这些数组是使用以下方式进行编码的:
var pageUsesClearBackground: Array<Bool> = [true, true, true, true]
coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
使用以下方式尝试解码:
pageUsesClearBackground = coder.decodeObject(forKey: "CB_KEY") as? Array<Bool>
会返回一个包含0个值的数组,尽管如果使用非NSSecureCoding版本,数组会被填充。
pageUsesClearBackground [Bool] 0 values
我怀疑这与Swift中的数组是一个结构体而不符合NSCoding有关。奇怪的是,多年来对它们进行编码和解码都能正常工作。有没有办法从NSSecureCoding之前创建的存档中检索类成员数组?
显然,NSSecureCoding非常安全,甚至无法获取数据。
Bingo - HangarRash的答案如下是正确的。您必须添加类所需的所有类。我创建了一个较小的测试应用程序,并将问题归档到了其中。当我运行较小的应用程序时,控制台中会出现以下警告:
NSCoderTest[11222:509325] [general] *** -[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber' (0x2140200a8) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set: '{
"NSCoderTest.Test" (0x100eb0700) , "NSArray" (0x213ff2440) [/System/Library/Frameworks/CoreFoundation.framework]"
}'. This will be disallowed in the future.
我正在处理的应用程序是在几个版本之前的Xcode中创建的,在Xcode 14.1下没有发出任何警告。
嗨,Apple,也许文档要多做一些工作,表情符号要少做一些工作?
英文:
Since
NSKeyedUnarchiver.unarchiveTopLevelObjectWithData
has been deprecated as of macOS 14.0 I am trying to load legacy NSCoder archives now using NSSecureCoding set in the class and using:
coder.unarchivedObject(ofClasses classes: [AnyClass], from data: Data)
to load the archive. The archive loads and most of the class members can be extracted. However, Swift Arrays always come out nil even though
coder.containsValue(forKey:)
returns true. The arrays were encoded with:
var pageUsesClearBackground: Array<Bool> = [true, true, true, true]
coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
Using:
pageUsesClearBackground = coder.decodeObject(forKey: "CB_KEY") as? Array<Bool>
Brings back an array with 0 values, even though if I use the non NSSecureCode version, the Array is populated.
pageUsesClearBackground [Bool] 0 values
I suspect this has something to do with Arrays in Swift are a struct and do not conform to NSCoding. Odd that it worked encoding and decoding them for years. Any idea how to retrieve class members that are Arrays from an archive made before NSSecureCoding?
Apparently, NSSecureCoding is so secure you can't even get the data back.
Bingo - HangarRash's answer below is correct. You have to add all the classes your class needs. I created a smaller test app and moved the offending archiving problem to it. When I ran the smaller app it gave the following warning in the console:
NSCoderTest[11222:509325] [general] *** -[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber' (0x2140200a8) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set: '{(
"'NSCoderTest.Test' (0x100eb0700) , "'NSArray' (0x213ff2440) [/System/Library/Frameworks/CoreFoundation.framework]"
)}'. This will be disallowed in the future.
The app I was working on was created several versions of Xcode ago and under Xcode 14.1 it was not emitting any warnings.
Hey Apple, maybe a little more work on the documentation and a little less work on emojis?
答案1
得分: 1
这是一些示例代码,您可以在Swift Playground中运行它,以解档您的数组:
class Foo: NSObject, NSSecureCoding {
static var supportsSecureCoding: Bool { true }
override init() {
super.init()
}
func encode(with coder: NSCoder) {
// This is based on the array in your question
var pageUsesClearBackground: Array<Bool> = [true, true, true, true]
coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
}
required init?(coder: NSCoder) {
// This happily unarchives the [Bool]
if let pageUsesClearBackground = coder.decodeObject(of: [NSArray.self, NSNumber.self], forKey: "CB_KEY") as? [Bool] {
print(pageUsesClearBackground) // Shows expected output
} else {
print("oops")
}
}
}
let foo = Foo()
let data = try NSKeyedArchiver.archivedData(withRootObject: foo, requiringSecureCoding: true)
let bar = try NSKeyedUnarchiver.unarchivedObject(ofClass: Foo.self, from: data)
NSSecureCoding
和相关的类都基于Objective-C。您需要指定支持NSCoding
(NSSecureCoding
)的类型。因此,在解档时,您需要告诉解档器您有一个NSArray
的NSNumber
实例。这就是Swift中的[Bool]
实际上被转换成的内容。
英文:
Here's some sample code you can run in a Swift Playground that allows you to unarchive your array:
class Foo: NSObject, NSSecureCoding {
static var supportsSecureCoding: Bool { true }
override init() {
super.init()
}
func encode(with coder: NSCoder) {
// This is based on the array in your question
var pageUsesClearBackground: Array<Bool> = [true, true, true, true]
coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
}
required init?(coder: NSCoder) {
// This happily unarchives the [Bool]
if let pageUsesClearBackground = coder.decodeObject(of: [NSArray.self, NSNumber.self], forKey: "CB_KEY") as? [Bool] {
print(pageUsesClearBackground) // Shows expected output
} else {
print("oops")
}
}
}
let foo = Foo()
let data = try NSKeyedArchiver.archivedData(withRootObject: foo, requiringSecureCoding: true)
let bar = try NSKeyedUnarchiver.unarchivedObject(ofClass: Foo.self, from: data)
NSSecureCoding
and related classes are all based on Objective-C. You need to specify types that support NSCoding
(NSSecureCoding
). So when unarchiving you need to tell the unarchiver that you have an NSArray
of NSNumber
instances. That is what the Swift [Bool]
is really converted into.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论