2015-03-16 3 views
4

У моего объекта есть целочисленный идентификатор. Так как это обязательное свойство я не определив его как необязательный и я требовать его в специально отведенном инициализаторе:Инициализатор удобства с необязательным свойством

class Thing { 

    var uniqueID: Int 
    var name: String? 

    init (uniqueID: Int) { 
     self.uniqueID = uniqueID 
    }  

} 

Поскольку я создаю один из них с некоторой JSON, использование вдоль линий:

if let uniqueID = dictionary["id"] as? Int { 
    let thing = thing(uniqueID: unique) 
} 

(Я бы с удовольствием проверил, что у меня до сих пор, между прочим).

Далее, я хотел бы иметь возможность добавить инициализатор удобства в класс Thing, который принимает объект словаря и соответствующим образом задает свойства. Это включает в себя необходимые uniqueID и некоторые другие дополнительные свойства. Моя лучшая работа до сих пор является:

convenience init (dictionary: [String: AnyObject]) { 
    if let uniqueID = dictionary["id"] as? Int { 
     self.init(uniqueID: uniqueID) 
     //set other values here? 
    } 
     //or here? 
} 

Но, конечно, это не является достаточным, так как назначенный инициализатор не вызывается на всех путях условной.

Как я должен обрабатывать этот сценарий? Возможно ли это? Или я должен согласиться с тем, что uniqueID должен быть необязательным?

ответ

4

Лучшим решением, вероятно, является использование отказоустойчивого инициализатора, который либо вернет экземпляр объекта, либо nil.

Поскольку объекты Swift не могут быть частично сконструированы, а инициализаторы удобства должны вызвать инициализатор без удобства, мы все равно должны что-то сделать в случае сбоя.

Результат будет выглядеть примерно так:

convenience init?(dictionary: [String: AnyObject]) { 
    if let uniqueID = dictionary["id"] as? Int { 
     self.init(uniqueID: uniqueID) 
    } else { 
     self.init(uniqueID: 0) 
     return nil 
    } 
} 

Вообще говоря, не-удобства инициализатор (ы) должен быть тот, который принимает все аргументы, и удобство Инициализаторы должны быть методы, которые Дон» t требуют некоторых аргументов.

Например, я мог бы сделать мой по умолчанию инициализатора выглядеть следующим образом:

init(uniqueID: Int, name: String? = nil) { 
    self.uniqueID = uniqueID 
    self.name = name 
} 

Это позволяет вызвать конструктор несколько различных способов:

let thing1 = Thing(1) 
let thing2 = Thing(2, nil) 
let thing3 = Thing(3, "foo") 
let thing4 = Thing(4, myUnwrappedStringVar) 
let thing5 = Thing(5, myWrappedStringOptional) 

И это уже охватывает много для нас.

Итак, давайте добавим еще один инициализатор удобства, который принимает необязательный номер Int.

convenience init?(uniqueID: Int? = nil, name: String? = nil) { 
    if let id = uniqueID { 
     self.init(uniqueID: id, name: name) 
    } else { 
     self.init(uniqueID: 0) 
     return nil 
    } 
} 

Теперь мы можем взять Int? для нашего uniqueID аргумента и просто не в состоянии, когда он nil.

Итак, еще один, чтобы принять словарь.

convenience init?(dictionary: [String: AnyObject]) { 
    let uniqueID = dictionary["id"] as? Int 
    let name = dictionary["name"] as? String 
    self.init(uniqueID: uniqueID, name: name) 
} 

У нас еще есть, то немного странно Initialize вернуться nil шаблон в нашем первом конструкторе удобства, но все остальное мы строим на вершине этого можно просто вызвать что удобство инициализатору и не требует странный узор.

В инициализаторе, который принимает словарь, если нет id ключа, или, если это то, что не в Int, то let uniqueID будет nil, поэтому, когда мы называем другой конструктор, он будет вызывать тот, который принимает Int?, пройдем nil, возврат nil, и поэтому тот, который мы позвонили, вернется nil.

+0

Я вижу, спасибо. Вы считаете, что это необычная настройка? Я спрашиваю, потому что это некоторые из первых Swift, которые я написал, поэтому я хотел бы использовать «стандартные» шаблоны везде, где это возможно (и где они существуют). Как бы вы справились с этим, если бы это был ваш код? Просто позвольте идентификатору быть дополнительным? –

+1

Я не думаю, что это слишком странно. То, что я сделаю полностью, зависит от гораздо большего контекста. Если вы действительно заинтересованы в том, чтобы найти лучший способ сделать это, вы всегда можете задать вопрос на [codereview.se] с рабочей версией вашего кода. – nhgrif

+1

@BenPackard Я расширил свой ответ. Возможно, это поможет вам. Внедрите эти методы как таковые и установите контрольную точку для отслеживания пути выполнения через этот код при инициализации со словарем. – nhgrif

5

У вас есть пара вариантов с этим. Одним из них является failable initialisers:

convenience init?(dictionary: [String: AnyObject]) { 
    if let uniqueID = dictionary["id"] as? Int { 
     self.init(uniqueID: uniqueID) 
    } else { 
     self.init(uniqueID: -1) 
     return nil 
    } 
} 

Технически это может быть переделано немного (в основном, в зависимости от ваших предпочтений/версии Свифта), но мое лицо предпочтения нечто следующим образом:

class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? { 
    if let uniqueID = dictionary["id"] as? Int { 
     return self.init(uniqueID: uniqueID) 
    } 

    return nil 
} 

Все вместе, как детская площадка:

class Thing { 
    var uniqueID: Int 
    var name: String? 

    init(uniqueID: Int) { 
     self.uniqueID = uniqueID 
    } 

    convenience init?(dictionary: [String: AnyObject]) { 
     if let uniqueID = dictionary["id"] as? Int { 
      self.init(uniqueID: uniqueID) 
     } else { 
      self.init(uniqueID: -1) 
      return nil 
     } 
    } 

    class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? { 
     if let uniqueID = dictionary["id"] as? Int { 
      return self.init(uniqueID: uniqueID) 
     } 

     return nil 
    } 
} 

let firstThing = Thing(uniqueID: 1) 
let secondThing = Thing(dictionary: ["id": 2]) 
let thirdThing = Thing(dictionary: ["not_id": 3]) 
let forthThing = Thing.fromDictionary(["id": 4]) 
let fithThing = Thing.fromDictionary(["not_id": 4]) 
+0

Я вижу - поэтому словарь-строитель больше не является инициализатором. Я так считал. Черт возьми, теперь я должен переосмыслить, почему инициализаторы удобства даже используются вообще. Вернемся к документам. –

+0

@BenPackard Удобные инициализаторы полезны в нескольких случаях, но я обычно использую их только тогда, когда они не поддаются проверке, поскольку инициализаторы Swift являются немного больными из-за необходимости давать значение каждому сохраненному свойству. –

+0

@nhgrif В этом случае я предлагаю удалить словарь-принимающий инициализатор и вместо этого выбрать функцию класса для выполнения аналогичного задания, оставив единственный метод init тем, который принимает все аргументы и не может потерпеть неудачу, поскольку вы предлагаете ? –

Смежные вопросы