Типизация в Swift
Я пишу библиотеку, которая может анализировать типизированные идентификаторы из JSON. Тем не менее, я считаю, что правила приведения типов немного озадачивают.
Пример:
class AccountId : NSString { }
let json : AnyObject? = "user-1" // Returned by NSJSONSerialization.JSONObjectWithData
let s = json as? NSString // Succeeds, s == Some("user-1")
let a = json as? AccountId // Fails, a == nil
Почему первый тип будет успешным, а второй - неудачным? Есть ли что-то волшебное в NSString
, которое не пересекает классы Swift?
Я использую XCode Version 6.1 (6A1030) (последний на момент написания).
Ответы
Ответ 1
Как правило, если у вас есть иерархия классов A → B → C (C наследует от B, которая, в свою очередь, наследуется от A), и у вас есть экземпляр B, вы можете повышать до A, но вы не можете понизить до C.
Причина в том, что C может добавлять свойства, недоступные в B, поэтому компилятор или среда выполнения не будут знать, как инициализировать дополнительные данные, не говоря уже о том, что они также должны быть выделены.
Обратите внимание, что полиморфизм позволяет вам делать upcast и downcast с переменными - это означает, что если у вас есть экземпляр C, хранящийся в переменной типа A, вы можете применить эту переменную к B и C, потому что на самом деле она содержит экземпляр of C. Но если переменная содержит экземпляр B, вы можете понизить до B, но не до C.
В вашем случае, а не в downcasting, вы должны специализировать конструктор, принимающий NSString
, но я подозреваю, что в этом конкретном случае NSString
он не может быть выполнен (там нет NSString
назначенный инициализатор, принимающий строку в качестве аргумента), Если вы в состоянии это сделать, тогда ваш код будет выглядеть так:
var json: AnyObject? = "test"
if let string = json as? NSString {
let a = AccountId(string: string)
}
и в этот момент вы можете использовать a
, где ожидается экземпляр либо AccountId
, либо NSString
Ответ 2
Есть ли что-то волшебное в NSString, которое не пересекается с классами Swift?
Да. Класс Swift String
автоматически соединяется с NSString
и наоборот. Из документы:
Swift автоматически соединяет между строковым типом и NSString класс. Это означает, что везде, где вы используете объект NSString, вы можете используйте вместо этого тип Swift String и получите преимущества обоих типы - интерполяция типов строк и Swift-разработанные API и Широкие функциональные возможности классов NSString. По этой причине вы должны почти никогда не нужно использовать класс NSString непосредственно в вашем собственном коде. Фактически, когда Swift импортирует API Objective-C, он заменяет все Типы NSString со строковыми типами. Когда ваш код Objective-C использует Swift, импортер заменяет все типы строк на NSString в импортированном API.
Почему первый тип будет успешным, а второй - неудачным?
Из-за сильной связи между String
и NSString
компилятор знает, как конвертировать из одного в другой. С другой стороны, поскольку ваш AccountId
класс является более специализированной версией NSString
, вы не можете отбрасывать от String
до AccountId
.