22027

[code]

    class Graphic: UIView {
    
        var _context: CGContextRef!
        var w: Float!
        var h: Float!
        var photo: Photo
    
    
    override init() {
        super.init(frame: CGRectZero)
        w = Float(self.frame.width)
        h = Float(self.frame.height)
        photo.initialize(CGFloat(w), h: CGFloat(h))
    
    }
    
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
[/code]

Photoは自作クラスで特に何も継承していません。  
このとき、

[code]

    super.init(frame: CGRectZero)
    
[/code]

において、

[code]

    Property self.photo not initialized at super init call
    
[/code]

というエラーが表示されます。  
また、photo の宣言がなかったとしてもfatalErrorになってしまいます。

ここで質問なのですが、まず、UIViewにおいてのコンストラクタのoverrideと、ある自作クラスを宣言した上でのUIViewにおけるその取り扱いを教えていただけないでしょうか？  
どうぞよろしくお願いいたします。


CC BY-SA 3.0
2016-02-12T14:28:55.910
22014
2016-02-13T06:02:55.530
2016-02-12T15:04:33.203
13272
13272
question
swift
UIViewを継承したサブクラスにおけるコンストラクタについて。


Swiftでは、コンストラクタ（Constructor）と呼ばずに、イニシアライザ（ **Initializer** ）と呼びます。

[The Swift Programming Language (Swift
2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html)

Apple自身によるSwiftの解説文書では、いちどもConstructorという単語は出てきません。

* * *

`UIView`のサブクラスの実装のしかたですが、(1) _プロパティの宣言_ 、(2) _イニシアライザの実装_
のふたつに分けて説明したいと思います。たがいに密接に関係していることですが、分けてみていったほうが、理解しやすくなるだろうと思います。

**プロパティの宣言**  
クラスのプロパティは、その型の種類と、初期値の有無で、つぎのように分類できます。  
(1) Optional型で宣言する。  
ごぞんじのように、`nil`である可能性のある変数を、Optional型の変数といい、`var image:
UIImage?`というように、型名に`?`を付して、Optional型であることを示します。宣言時、暗黙的に`nil`が代入されるので、`var
image: UIImage? =
nil`と同等です。Optional型のプロパティは、未設定という状態を示す必要があるケースで使われます。たとえば、`UIImageView`のプロパティ`image`がOptional型ですが、イメージのない空白の`UIImageView`のインスタンスの状態が、じっさいに存在するので、Optional型となっています。  
(2) Implicitly unwrapped optional型で宣言する。  
これもご存じのように`!`を型名に付して示します。例：`@IBOutlet weak var imageView:
UIImageView!`。この型の宣言は、特殊なケースを除いて推奨されません。そもそもImplicitly unwrapped
optional型は、プログラマの責任において、まちがいなく`nil`にならないことが保証されるケース以外で、使うべきでないからです。プロパティにおける特殊なケースで有名なのは、XIB書類、Storyboardのオブジェクトに紐付けされたプロパティですが、XIB書類、Storyboardが、その存在を定義しているため（存在が保証されるため）だと考えられます。  
(3) 非Optional型で、初期値を与える。  
プロパティでなく、一般の変数の宣言においても、非Optional型の変数は、宣言時に初期値を与える必要があります。それとおなじです。例：`var x:
Float = 0.0`。Optional型のプロパティ以外は、できるだけこの形で宣言すべきと、わたしは考えます。  
(4) 非Optional型で、初期値を与えない宣言。  
指定イニシアライザで、プロパティに値が代入されるケースに限って、非Optional型のプロパティでも、初期値をなしにできます。ご提示のコード`var
photo: Photo`がそれにあたりますが、イニシアライザの中で、`Photo`インスタンスを生成して、代入していないため、

> Property self.photo not initialized at super init call

というエラーが起きているのだと考えられます。

**イニシアライザの実装**  
クラスのイニシアライザ（Initializer）は、指定イニシアライザ（Designated
Initializer）と、簡易イニシアライザ（Convenience
Initializer）に分けられます。クラスはかならず指定イニシアライザでインスタンスを生成します。簡易イニシアライザは、それ自身ではインスタンス生成はできず、その中で指定イニシアライザを呼ぶことで、インスタンス生成を可能にしています。簡易イニシアライザは、頭に`convenience`を付します。`UIView`では、`init(frame
frame: CGRect)`が指定イニシアライザです。なお、XIB書類、Storyboardから生成するときは、`init(coder aDecoder:
NSCoder)`が呼ばれます。

**イニシアライザの実行**  
イニシアライザは、ほかの一般的なメソッドと異なる面を持ちますが、実行するコードも特殊な書式になります。

[code]

    let newView = UIView.init(frame: newRect)
    
[/code]

このような書式ではなく、

[code]

    let newView = UIView(frame: newRect)
    
[/code]

このように、クラス名に直接かっこをつないだものになります。

* * *

以上から、ご提示のクラス`Graphic`の定義コードの、ひとつの改善案はこうなります。

[code]

    class Graphic: UIView {
    
        var context: CGContextRef? // Appleのプライベート識別子とバッティングする危険があるので、語頭にアンダーバーを付けない。
        var w: Float = 0.0
        var h: Float = 0.0
        var photo: Photo
    
        override init(frame frame: CGRect) {
            super.init(frame: frame)
    
            w = Float(self.frame.width)
            h = Float(self.frame.height)
            photo = Photo(CGFloat(w), h: CGFloat(h))
        }
    
        convenience init() {
            self.init(frame: CGRectZero)
        }
    
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            w = Float(self.frame.width)
            h = Float(self.frame.height)
            photo = Photo(CGFloat(w), h: CGFloat(h))
        }
    
    }
    
[/code]

これはあくまでひとつのアイデアです。各プロパティの役割を知らない状態で、推測に基づいて考えたものであって、それ以上のものではありません。


CC BY-SA 3.0
2016-02-13T04:34:47.243
22027
2016-02-13T04:43:20.037
2016-02-13T04:43:20.037
7362
7362
22014
answer


気になったところを1点だけ回答します。

> Property self.photo not initialized at super init call

このエラーですが、サブクラスで定義したプロパティをすべて初期化する前に、スーパークラスのイニシャライザを呼び出しているのが原因です。

photo以外のプロパティはimplicitly unwrapped
optionalになっているため、暗黙的にnilで初期化されていますが、photoは宣言だけで初期化されていないためエラーになります。

可能であれば、宣言と同時に初期化することをおすすめします。

以下のコードで、コンパイルが通るところまでは確認しました。  
（Xcode 7.2.1で確認）

[code]

    import UIKit
    
    class Photo {
        func initialize(w:CGFloat, h: CGFloat) {
        }
    }
    
    class GraphicsView: UIView {
    
        var _context: CGContextRef!
        var w: Float!
        var h: Float!
    
        // 宣言と同時に初期化
        var photo = Photo()
    
        init() {
            super.init(frame: CGRectZero)
            w = Float(self.frame.width)
            h = Float(self.frame.height)
            photo.initialize(CGFloat(w), h: CGFloat(h))
        }
    
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
[/code]


CC BY-SA 3.0
2016-02-13T06:02:55.530
22033
2016-02-13T06:02:55.530
7459
22014
answer