2015-05-28 2 views
1

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

class Test : UIView { 

    let subview: UIView 

    override init(frame: CGRect) { 
     subview = UIView() // once 
     super.init(frame: frame) 
    } 

    required init(coder aDecoder: NSCoder) { 
     subview = UIView() // twice 
     super.init(coder: aDecoder) 
    } 

} 

Если я пытаюсь использовать общий инициализаторе тогда я получаю следующие ошибки (см комментарии)

override init(frame: CGRect) { 
    commonInit() // 1: Use of 'self' in method call 'commonInit' before super.init initializes self 
    super.init(frame: frame) // 2: Property 'self.subview' is not in initialized at super.init call 
} 

private func commonInit() { 
    subview = UIView() // 3: Cannot assign to 'subview' in 'self' 
} 

Он отлично работает, если я не использовать константу и определить подвид, как :

var subview: UIView? 

И тогда, конечно, порядок переключатель инициализации, как это:

super.init(frame: frame) 
commonInit() 

Итак, мой вопрос: нет ли способа использовать обычный инициализатор для констант в Swift на данный момент?

EDIT: Я полностью забыл упомянуть, что борьба здесь заключается в том, что я не могу инициировать subview до того, как я в init, он инициирован на основе данных, которые неизвестны при объявлении константы.

+0

дубликата http://stackoverflow.com/questions/ 24023412/how-to-implement-two-inits-with-same-content-without-code-duplication-in-swift? –

+0

Нет, как я уже сказал, я знаю, как это сделать для var, я использую let – Johannes

+0

См. Мой ответ ниже. Он позволяет правильно инициализировать значения на основе контекста. –

ответ

0

Выполните следующие действия:

class Test : UIView { 

    let subview = UIView() 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     //edit subview properties as needed 
    } 
} 
2

Попробуйте это:

class Test : UIView { 

    let subview = UIView() 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 
} 
0

Один из вариантов после Xcode схемы:

class Test : UIView { 

    var subview: UIView! 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     commonInit() 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     commonInit() 
    } 

    func commonInit() { 
     subview = UIView() 
    } 
} 

замечает, что ваш subview является var

Другого варианта:

class Test : UIView { 

    let subview: UIView = { 
     let sv = UIView() 
     // some config, (i.e.: bgColor etc., frame is not yet _real_ 
     // can't yet access instance's frame and other properties 
     return sv 
    }() 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     commonInit() 
    } 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     commonInit() 
    } 

    func commonInit() { 
     // frame might be valid, preferably use layout constraints 
     addSubview(subview) 
    } 
} 

Надеется, что это помогает

+1

'subview' инициализируется еще до того, как вызывается' super.init() ', это на самом деле не« ленивый »:) - Возможно, вы имели в виду' lazy var subview: UIView = ... '(но тогда он не является постоянным больше) , –

+0

@MartinR вы правы, мое использование _lazy_ вводило в заблуждение. Я имел в виду _lazy commonInit() _: -/Отредактировал его, надеюсь, что более ясно, что я намеревался. Спасибо –

3

Другого варианта:

class Test : UIView { 

    let subview:UIView 

    init(frame: CGRect?, coder: NSCoder?) { 

     // The first phase initialization here 
     subview = UIView() 

     if let frame = frame { 
      super.init(frame: frame) 
     } 
     else if let coder = coder { 
      super.init(coder: coder) 
     } 
     else { 
      super.init() 
     } 

     // the Second phase initialization here 
     self.addSubview(subview) 
    } 

    convenience init() { 
     self.init(frame: nil, coder: nil) 
    } 

    override convenience init(frame: CGRect) { 
     self.init(frame: frame, coder: nil) 
    } 

    required convenience init(coder aDecoder: NSCoder) { 
     self.init(frame: nil, coder: aDecoder) 
    } 
} 

Немного уборщику альтернативы:

class Test : UIView { 

    let subview:UIView 

    private enum SuperInitArg { 
     case Frame(CGRect), Coder(NSCoder), None 
    } 

    private init(_ arg: SuperInitArg) { 

     subview = UIView() 

     switch arg { 
     case .Frame(let frame): super.init(frame:frame) 
     case .Coder(let coder): super.init(coder:coder) 
     case .None: super.init() 
     } 

     addSubview(subview) 
    } 

    convenience init() { 
     self.init(.None) 
    } 
    override convenience init(frame: CGRect) { 
     self.init(.Frame(frame)) 
    } 
    required convenience init(coder aDecoder: NSCoder) { 
     self.init(.Coder(aDecoder)) 
    } 
} 
+0

Я все еще боюсь ли закрыть вопрос как дубликат http://stackoverflow.com/questions/24023412/how-to-implement-two-inits-with-same-content-without-code-duplication-in -swift, который имеет аналогичный ответ http://stackoverflow.com/a/26772103/1187415 (даже если ваш взгляд выглядит немного чище). –

+0

Ничего себе, я не видел этого ответа. По крайней мере, мой ответ определенно является обманом. – rintaro

1

Это хорошо работает для м е:

// Declare this somewhere (it can be used by multiple classes) 
class FrameCoder: NSCoder { 
    let frame: CGRect 
    init(_ frame: CGRect) { 
     self.frame = frame 
     super.init() 
    } 
} 

Затем, когда вы хотите общий шаблон инициализатора, используйте:

class MyView: UIView { 

    let something: SomeType 

    required init(coder aDecoder: NSCoder) { 
     if (aDecoder is FrameCoder) { 
      super.init(frame: (aDecoder as! FrameCoder).frame) 
     } 
     else { 
      super.init(coder: aDecoder) 
     } 

     // Common initializer code goes here... 
     something = // some value 
    } 

    override init(frame: CGRect) { 
     self.init(coder: FrameCoder(frame)) 
    } 
} 

Преимущество использования этого метода вам не нужно создавать значения по умолчанию для let определений - вы можете установить их в правильные значения в контексте, точно так же, как если бы был только один инициализатор.

Обратите внимание, что вы можете использовать эту технику для инициализаторов, берущих произвольные значения (не только для init(frame: CGRect)) - вы можете создать определенный подкласс NSCoder для переноса любого значения и типа, которые необходимо передать инициализатору, а затем подключить его к ваш init(coder:) способ.

(также, там, наверное, какой-то способ сделать это с общим ... уже совсем не понял, что из еще! Любой ...?)

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