しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

UIViewのサブクラスで`init?(coder aDecoder: NSCoder)`が必要と言われる理由を調べてみる

UIViewでinitializerを実装すると、init?(coder aDecoder: NSCoder)も実装するように言われます。
今回はinit?(coder aDecoder: NSCoder)と言われる理由などを調べてみます。

f:id:llcc:20170713231749p:plain

init?(coder aDecoder: NSCoder)とはなにか

init?(coder aDecoder: NSCoder)NSCodingプロトコルで定義されているメソッドです。
UIViewはNSCodingに準拠しているので、このメソッドも必須になっています。

public protocol NSCoding {
    public func encode(with aCoder: NSCoder)
    public init?(coder aDecoder: NSCoder)
}

NSCodingプロトコルとは

NSCodingプロトコルとは、そのクラスをアーカイブできるようにするものです。
NSCodingに準拠すれば、以下のようにNSKeyedArchiverを使ってData型への変換をする事ができます。

class MyClass: NSObject, NSCoding {
    override init() {
        super.init()
    }
    
    required init?(coder aDecoder: NSCoder) {
        
    }
    
    func encode(with aCoder: NSCoder) {
        
    }
}

let data = NSKeyedArchiver.archivedData(withRootObject: MyClass())

UIViewもNSCodingに準拠しているのでアーカイブする事ができます。

let data = NSKeyedArchiver.archivedData(withRootObject: UIView())

NSCodingのメソッドはアーカイブ時、復元時に呼ばれます。

public protocol NSCoding {
    public func encode(with aCoder: NSCoder) // アーカイブする時に呼ばれる
    public init?(coder aDecoder: NSCoder) // アーカイブされたものを復元する時に呼ばれる
}

なぜinitにrequireが付くのか

initにrequireが付く件についてテストプロトコルを作って動かしてみました。
試したところ、protocolでinitを定義するとrequireになるようです。

protocol MyProtocol {
    init(test: Int)
}

class MyClass: MyProtocol {
    required init(test: Int) {
    }
}

optionalを付けるとどうなるかも試そうとしたのですが、コンパイルエラーになってしまいました。

@objc protocol MyProtocol {
    optional init(test: Int) // 'optional' cannot be applied to an initializerエラーになる
}

公式ドキュメントにも、requiredになると書いてありました。

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

なぜUIViewサブクラスでinitを定義するとinit?(coder aDecoder: NSCoder)が必要になるのか

最後に、initを定義した途端にinit?(coder aDecoder: NSCoder)が必要になる理由についても調べてみました。

こちらはSwiftの仕様でした。
Swiftでは、initializerが1件もない時は親クラスのrequiredを再定義する必要がありません。
そのため、UIViewのサブクラスでinitを定義した途端にinit?(coder aDecoder: NSCoder)が必要になったようです。

class MyClass {
    init() {
    }
    
    required init(test: Int) {
    }
}

class MySubclass: MyClass {
}