2017-04-19 33 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

相关问题