#swift
#swift
Вопрос:
Давайте рассмотрим эти два простых класса, чтобы показать мою проблему:
class Object{
var name:String?
// keep it simple... and useless
}
class TestClass {
var objects:AnyObject[]?
func initializeObjects (){
objects?.insert(Object(), atIndex:0) // Error
objects?.insert(Object(), atIndex:1) // Error
objects?.insert(Object(), atIndex:2) // Error
}
}
С помощью этой реализации я получаю 3 ошибки Could not find member 'insert'
, когда я пытаюсь добавить объект в objects
массив.
Теперь, если я удалю необязательное из objects
определения, и необязательная цепочка в initializeObjects
нем будет работать без проблем (здесь рабочий код)
class Object{
var name:String?
}
class TestClass {
var objects:AnyObject[] = AnyObject[]() // REMOVE optional and initialize an empty array
func initializeObjects (){
objects.insert(Object(), atIndex:0) // Remove Opt chaining
objects.insert(Object(), atIndex:1) // Remove Opt chaining
objects.insert(Object(), atIndex:2) // Remove Opt chaining
}
}
Я не могу понять, что не так в первой реализации.
Я думал, что он проверяет, objects?
если objects
нет nil
, и на этом этапе он добавляет элемент, используя insert:atIndex:
. Но я, вероятно, ошибаюсь — .-
Ответ №1:
Массивы в Swift — это структуры, а структуры — это типы значений.
Опции в Swift на самом деле являются перечислениями ( Optional<T>
или ImplicitlyUnwrappedOptional<T>
).
Когда вы разворачиваете необязательный (неявно или явно) тип значения, то, что вы получаете, на самом деле является постоянной копией структуры. И вы не можете вызывать mutating
методы для постоянной структуры.
Выполнение objects?.insert(Object(), atIndex:0)
в основном означает это:
if let tmp = objects {
tmp.insert(Object(), atIndex:0)
}
В качестве обходного пути вам необходимо присвоить развернутое значение переменной, а затем присвоить переменную вашему необязательному свойству. Вот как работают типы значений.
Это воспроизводимо для любой структуры, а не только для массивов:
struct S {
var value: Int = 0
}
var varS: S = S()
varS.value = 10 //can be called
let constS: S = S()
constS.value = 10 //cannot be called - constant!
var optionalS: S? = S()
optionalS?.value = 10 //cannot be called, unwrapping makes a constant copy!
//workaround
if optionalS {
var tmpS = optionalS!
tmpS.value = 10
optionalS = tmpS
}
Некоторые соответствующие обсуждения здесь: https://devforums.apple.com/thread/233111?tstart=60
Комментарии:
1. Можем ли мы сказать, что инициализация массива в классе является своего рода хорошей практикой, позволяющей избежать обходного пути? (кстати, отличный ответ!)
2. @MatterGoal или
NSArray
? Недавно об этом шла дискуссия на форумах разработчиков Apple. Также, похоже, возникает ошибка при вызовеarray?[index] = something
, которая компилируется, но ничего не делает.3. Откуда вы взяли это «Когда вы разворачиваете необязательный (неявный или явный) тип значения, то, что вы получаете, на самом деле является постоянной копией структуры» Я не могу найти это в руководстве! Это действительно важная информация.
4. @MatterGoal Это важно и недокументировано. Это связано с тем, что опции реализуются с использованием
enum
. Это не просто синтаксический сахар. Даже перечисление должно создавать копию типа значения при возврате. И постоянство возвращаемого значения связано с тем, что это временная переменная — если вы не присвоите ее чему-либо, вы не сможете ее изменить, потому что изменение будет потеряно.5. @Sulthan, не должна ли строка
optionalS?.value = 10
поoptionalS!.value = 10