2017-04-19 10 views
3

私はクロージャでカスタム関数を実装しようとしました。しかし、それは#selectorでサポートされていません。#selectorはクロージャと互換性がありませんか?

ここでは例です:

class Core: NSObject { 

    static let shared:Core = Core.init() 


    func button(viewController: UIViewController, button: UIButton, title: String, color: UIColor, completion:() -> Void) { 

     button.layer.cornerRadius = button.bounds.width/2 
     button.setTitle(title, for: .normal) 
     button.setTitleColor(UIColor.white, for: .normal) 
     button.backgroundColor = color 
     button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18) 
     button.addTarget(viewController, action: #selector(completion()), for: .touchUpInside) 
    } 
} 

Xcodeは私に、ビルド時の問題を与える: '#selector' の

引数は '@objc' メソッド、プロパティを参照していない、または初期化子

答えて

3

セレクタは、Objective Cランタイムのメソッド、プロパティ、イニシャライザを識別するために使用される文字列です。 #selector(SomeClass.SomeMethod(withParam:AndParam:)のような表記法を使用する場合は、コンパイラが簡単に解析して正しいことを検証できる形式でセレクタを指定します。しかし、最終的には、それはちょうど"SomeMethodwithParam:AndParam:"のようなCの文字列に縮小されます。

基本的に、すべてのクラスには、セレクタを実装するコードの関数ポインタにセレクタをマップする辞書があります。セレクタを使用して関数を呼び出すと、Objective Cランタイムはメソッドテーブルで問題のクラスを検索し、指定されたセレクタに対応するメソッド実装をルックアップします。

このプロセスは、定義によって匿名のクロージャでは動作しません。したがって、セレクタは、Objective Cランタイムに登録されているメソッド、プロパティ、初期化子(暗黙的または明示的には@objc)を参照するためにのみ使用できます。

1

このようにして補完ブロックを呼び出すことはできません。 #selectorは、プロジェクトのどこかのクラスで定義された関数です。クロージャは有効なセレクタではありません。

完了ブロックをtypealiasと宣言し、補完をクラスのプロパティとして保存します。

// Declare your completion as typealias 
typealias YourCompletion =() -> Void 

// At top of your class 
var completion: YourCompletion? 

// Then in your function declare the completion: parameter to be of type YourCompletion 
func button(viewController: UIViewController, button: UIButton, title: String, color: UIColor, completion: YourCompletion) { 

    // Assign completion as property 
    self.completion = completion 

    // Configure your button 
    button.layer.cornerRadius = button.bounds.width/2 
    button.setTitle(title, for: .normal) 
    button.setTitleColor(UIColor.white, for: .normal) 
    button.backgroundColor = color 
    button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18) 

    // Have your button action be the function you'll make below 
    button.addTarget(viewController, action: #selector(self.callCompletion), for: .touchUpInside) 
} 

func callCompletion() { 
    if let completion = self.completion { 
     // Call completion 
     completion() 
    } 
} 
+0

プロパティを使用して、このような完了ハンドラを格納することは、実際には***脆弱です。スレッドセーフではなく、状態エラーが発生しやすい – Alexander

+0

はい、これは明らかに@Mannopsonの質問に対する簡単な簡単な答えです。現在のスレッドに対する手動による検査の中には、マルチスレッドの状況で役立つものがあります。それとも、メインスレッドにとどまっても問題ありません。しかし、まずはMannopsonが正しい方向に向かうでしょう。 –

+1

私は同意しません、これは確かに正しい方向ではありません。これは落とし穴を完全に理解していないかもしれない誰かの手にとって危険なアプローチです。私が考えることができる唯一の方法は、新しいクラスをインスタンス化し、クロージャー本体を一意のセレクタを持つメソッドとして登録することです。 – Alexander

関連する問題