iOS 如何让一个函数接受确认到 NSCoding 的参数

iOS How to make a function to accept parameter confirming to NSCoding

我有一个函数可以将确认 NSCoding 的基元和对象保存到 UserDefaults

func set(_ value: Any?, forKey key: String) {
    if let value = value {
        if (value is NSCoding) {
            let encodedValue = NSKeyedArchiver.archivedData(withRootObject: value)
            UserDefaults.standard.set(encodedValue, forKey: key)
        } else {
            print("Storage: failed to save value for key \(key). Value must confirm to NSCoding protocol")
        }
    } else {
        UserDefaults.standard.removeObject(forKey: key)
    }
}

我想去掉函数内部的一致性检查。所以函数必须只接受 NSCoding 个值。

我尝试将函数的签名更改为:

func set(_ value: NSCoding?, forKey key: String)

还有这个:

func set<T: Any>(_ value: T?, forKey key: String) where T: NSCoding 

但是,我在每次函数调用中都会遇到编译错误。

在第一种情况下我得到:

Argument type ‘***’ does not conform to expected type 'NSCoding?'

第二个:

Cannot convert value of type '***' to expected argument type '_?'

我应该更改什么才能使其正常工作?

方法声明本身似乎没有问题

func set<T: Any>(_ value: T?, forKey key: String) where T: NSCoding {
    if let value = value {
            let encodedValue = NSKeyedArchiver.archivedData(withRootObject: value)
            UserDefaults.standard.set(encodedValue, forKey: key)
    } else {
        UserDefaults.standard.removeObject(forKey: key)
    }
}

但编译错误指出您传递给此函数的值可能与 NSCoding

不兼容

使用

调用上述函数的示例
self.set("abcd", forKey: "test")

给出错误

Cannot convert value of type 'String' to expected argument type '_?'

另一方面称它为

self.set(NSString(string: "abcd"), forKey: "test")

工作正常。因此,请确保您传递了正确的参数 :)

编辑 1:

根据 OP 的评论,使用 Int

测试了泛型方法
    let test: Int = 100
    self.set(test, forKey: "test")

导致相同的错误,尽管将其装箱到 NSNumber

    let test: Int = 100
    self.set(NSNumber(value: test), forKey: "test")

编辑 2:

正在回答 OP 的评论

But why String values pass if (value is NSCoding) check in my original function?

    let a: Any = "abcd"
    if a is NSCoding {
        debugPrint("am here")
    }

那是因为您的 any 通过了 NSCoding 检查,在您的情况下,无论您传递给函数的值是什么,您都将其类型强制转换为 Any 并且 Any 通过了检查: )

因此它在你的第一个方法中工作但失败了泛型:) 希望对你有帮助

也许你可以一起跳过 NSCoding

func set(_ value: Any?, forKey key: String) throws {
    if let value = value {
        let encodedValue = try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
        UserDefaults.standard.set(encodedValue, forKey: key)
    } else {
        UserDefaults.standard.removeObject(forKey: key)
    }
}

(请注意,我更改为未弃用的方法,但您可以随意改回)

例子

do { 
    try set("abc", forKey: "k1")
} catch {
    print(error)
}