31713


以下のようなコードで変数に保存したインスタンスメソッドを実行したいのですが、コンパイルエラーになります。こういう場合どのように実行すればいいのでしょうか。

[code]

    class Sample {
        let myMethod = sampleMethod
    
        func sampleMethod() {
        }
    
        func callMethod() {
            // myMethodを実行したいがコンパイルエラーになる
            myMethod()
        }
    }
    
[/code]

また、myMethodの型は「(Sample) -> () -> ()」になっていました。  
() ->
()のように括弧２つをアローで繋ぐのがクロージャーということは知っているのですが、上記のように括弧３つアロー２つはどのような型を意味しているのでしょうか。

* * *

追記1

myMethod()のエラーメッセージを見ると、Sample型の引数が必要と出ていたので、myMethod(self)としてみたのですが、この場合は「Expression
resolves to an unsused function」というエラーが発生します。

* * *

追記2

セレクターを使えば似たことはできました。ただ、NSObjectを継承する必要があります。

[code]

    class Sample: NSObject {
        let myMethod = #selector(Sample.sampleMethod)
    
        func sampleMethod() {
            print("wanwan")
        }
        func callMethod() {
            self.perform(myMethod)
        }
    }
    
[/code]


CC BY-SA 3.0
2017-01-08T13:26:23.480
31711
2017-01-08T13:56:49.633
2017-01-08T13:46:42.320
8148
8148
question
swift swift3
変数に保存したインスタンスメソッドを実行したい


あなたのクラス定義中で、この行:

[code]

    let myMethod = sampleMethod
    
[/code]

これは、こう書くのと同じことになります。

[code]

    let myMethod = Sample.sampleMethod
    
[/code]

Swiftのインスタンスプロパティの初期化式では`self`を使えないため、右辺の初期値はクラスコンテキストで解釈されるため、通常のインスタンスコンテキストとは違う解釈になります。

(インスタンスコンテキストだと、単に`sampleMethod`と書くと、`self.sampleMethod`と同じ意味になる。)

で、その`Sample.sampleMethod`は一体どんなものなのかというと、表示されている型名が物語っているのですが、`(Sample) -> ()
-> ()`と言うのは、「`Sample`型の引数をとって、`() ->
()`型の結果を返すクロージャー」になります。(つまり一度呼び出すと、その結果がまたクロージャー。)

(この部分Swift 4では(間に合えばSwift 3.1かも)変更になる予定です。)

つまり結果として返されたクロージャーにもう一度、`()`を引数として与えないと、本来のメソッドが実行できない、と言うことになります。

[code]

    func callMethod() {
        // `myMethod`に`Sample`型の値を渡した結果(クロージャー)に`()を渡して呼び出す
        myMethod(self)()
    }
    
[/code]

お試しください。

* * *

(補足)

`Sample`クラスの`init`の中(インスタンスコンテキストになります)で`myMethod`への代入が行われるように書き換えれば、`myMethod`の中身は`()`を渡すだけで呼び出せる普通のクロージャーになるのですが、その際は厄介な循環参照の問題が発生します。

これはインスタンスメソッドをクロージャーとして使う場合に、クロージャー内に`self`への強参照が保持されるためです。この辺り、せっかくメソッドが簡単にクロージャーとして扱えるSwiftではちょっとまどろっこしいところですが、まぁ、「インスタンスメソッドを直接同じインスタンスのプロパティに代入してはいけない」と言うのはしっかり覚えておいてください。


CC BY-SA 3.0
2017-01-08T13:56:49.633
31713
2017-01-08T13:56:49.633
13972
31711
answer