2017-04-19 54 views
3

我試圖用閉包實現自定義函數。但它不支持#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:)這樣的符號時,您可以用編譯器可以輕鬆解析並驗證正確的格式指定選擇器。但最終,這隻會被縮減爲C字符串,如:"SomeMethodwithParam:AndParam:"

本質上,每個類都有一個字典,它將選擇器映射到實現它們的代碼的函數指針。當使用選擇器來調用函數時,Objective C運行庫在方法表中搜索相關類,並查找與給定選擇器相對應的方法實現。

此過程無法與閉包一起使用,閉包定義爲匿名。因此,只能使用選擇器來引用在Objective C運行時註冊的方法,屬性和初始化程序(這是@objc隱含或顯式執行的操作)。

1

您不能以這種方式調用完成塊。 A #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問題的簡單快速回答。針對當前線程的某些手動檢查在多線程情況下會很有用。或者留在主線上,你會沒事的。但對於初學者來說,這將使曼諾普森朝着正確的方向前進。 –

+1

我不同意,這當然不是正確的方向。這是一個危險的方法,可能無法完全理解陷阱的人。如果你真的想要將Crowbar封閉到ObjC API中,那麼我能想到的唯一方法是實例化一個新類,並將封閉體註冊爲具有唯一選擇器的方法。 – Alexander