iOS Как заставить функцию принимать подтверждение параметров в NSCoding - PullRequest
0 голосов
/ 01 февраля 2019

У меня есть функция, которая сохраняет примитивы и объекты, подтверждающие от 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 

Тем не менее, я получаю ошибку компиляции при каждом вызове функции.

В первом случае я получаю:

Тип аргумента '***' не соответствует ожидаемому типу 'NSCoding?'

Всекунда:

Невозможно преобразовать значение типа '***' в ожидаемый тип аргумента '_?'

Что я должен изменить, чтобы это работало?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Может быть, вы можете пропустить 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)
}
0 голосов
/ 01 февраля 2019

Кажется, что нет никаких проблем в самом объявлении метода

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")

выдает ошибку

Невозможно преобразовать значение типа 'String' в ожидаемый тип аргумента '_?'

С другой стороны, называя его

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

Работает нормально.Поэтому убедитесь, что вы передали верный аргумент :)

РЕДАКТИРОВАТЬ 1:

На основании комментария ОП проверен метод Generics с Int

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

Приводит к той же ошибке, помещая ее в NSNumber, хотя

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

РЕДАКТИРОВАТЬ 2:

Ответ на комментарий OP

Но почему Stringзначения проходят, если (значение NSCoding) проверяется в моей исходной функции?

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

Это потому, что ваш any проходит проверку NSCoding, и в вашем случае, независимо от того, какое значение вы передаете набираемой функцииприведите его к Any и Any пройдет через проверку :)

Следовательно, он работает в вашем первом методе, но не работает с обобщениями :) Надеюсь, это поможет

...