#selector Swift 2.2 中的处理程序方法

Handler Method in #selector Swift 2.2

我的想法是创建一种向按钮添加事件方法的简单方法。

以我为例: Gist

问题是我遇到了 EXC_BAD_ACCESS 的问题,可能是因为#selector 中的执行不安全。

是否有修复或更改的方法来解决它?

调试屏幕:

堆栈跟踪:

* thread #1: tid = 0x4e8e43, 0x0000000110355547 libsystem_blocks.dylib`_Block_copy_internal + 239, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000110355547 libsystem_blocks.dylib`_Block_copy_internal + 239
frame #1: 0x000000010d8c326d Swift-201-Aula2-Exercicio2.1`@objc UICallbackButton.executeCallback(UIButton, callback : (button : UIButton) -> ()) -> () + 45 at ViewController.swift:0
frame #2: 0x000000010e2d4a8d UIKit`-[UIApplication sendAction:to:from:forEvent:] + 92
frame #3: 0x000000010e447e67 UIKit`-[UIControl sendAction:to:forEvent:] + 67
frame #4: 0x000000010e448143 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 327
frame #5: 0x000000010e447263 UIKit`-[UIControl touchesEnded:withEvent:] + 601
frame #6: 0x000000010e34799f UIKit`-[UIWindow _sendTouchesForEvent:] + 835
frame #7: 0x000000010e3486d4 UIKit`-[UIWindow sendEvent:] + 865
frame #8: 0x000000010e2f3dc6 UIKit`-[UIApplication sendEvent:] + 263
frame #9: 0x000000010e2cd553 UIKit`_UIApplicationHandleEventQueue + 6660
frame #10: 0x000000010d9d0301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #11: 0x000000010d9c622c CoreFoundation`__CFRunLoopDoSources0 + 556
frame #12: 0x000000010d9c56e3 CoreFoundation`__CFRunLoopRun + 867
frame #13: 0x000000010d9c50f8 CoreFoundation`CFRunLoopRunSpecific + 488
frame #14: 0x0000000112140ad2 GraphicsServices`GSEventRunModal + 161
frame #15: 0x000000010e2d2f09 UIKit`UIApplicationMain + 171
* frame #16: 0x000000010d8c42c2 Swift-201-Aula2-Exercicio2.1`main + 114 at AppDelegate.swift:12
frame #17: 0x000000011031292d libdyld.dylib`start + 1

老实说,我不知道如何用选择器来做,但这里是闭包。测试和工作 100%。我将尝试使用选择器找到解决方案。不可能有那么大的不同。可以传一个控制事件数组,它会占全部。

import UIKit

class ViewController: UIViewController {

    var count = 1

override func viewDidLoad() {
    super.viewDidLoad()

    // Button 1
    let button = GIBbutton(frame: CGRect(x: 60, y: 30, width: 200, height: 60), controlEvents: [.TouchUpInside], targetAction: { button in
        button.setTitle("Title \(self.count)", forState: .Normal)
        self.count += 1
    })
    button.backgroundColor = UIColor.orangeColor()
    button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
    button.setTitle("Title Default", forState: .Normal)


    self.view.addSubview(button)

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

}

class GIBbutton : UIButton{ var 动作:((UIButton!)->Void)!

convenience init(frame:CGRect,controlEvents:[UIControlEvents],targetAction:((UIButton!)->Void)!){
    self.init()
    self.frame = frame
    action = targetAction
    for event in controlEvents{
        self.addTarget(self, action: #selector(GIBbutton.performOurAction), forControlEvents: event)
    }
}

func performOurAction(button:UIButton,completion:(UIButton)){
    self.action(self)
}

}

我相信您的代码有几个问题。按钮上的循环引用(可能不是问题),但可以肯定的是,您的回调不会一直到 executeCallback(:) 方法。事实证明你不需要它。只需保存对回调的引用。看看这是否有意义。有效。

import UIKit

class ViewController: UIViewController {

    var count = 1

    override func viewDidLoad() {
        super.viewDidLoad()

        // Button 1
        let button = UICallbackButton(frame: CGRect(x: 60, y: 30, width: 200, height: 60))
        button.backgroundColor = UIColor.orangeColor()
        button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        button.setTitle("Title Default", forState: .Normal)
        button.on(.TouchUpInside, then: {
            button.setTitle("Title \(self.count)", forState: .Normal)
            self.count = self.count + 1
        })

        self.view.addSubview(button)


        // Button 2
        let button1 = UICallbackButton(frame: CGRect(x: 60, y: 160, width: 200, height: 60))
        button1.backgroundColor = UIColor.blackColor()
        button1.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        button1.setTitle("Value Default", forState: .Normal)
        button1.on(.TouchUpInside, then: {
            button1.setTitle("Value \(self.count)", forState: .Normal)
            self.count = self.count + 1
        })

        self.view.addSubview(button1)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}


typealias ButtonCallback = (() -> ())

protocol UIActionButton {

    func on(event : UIControlEvents, then callback: ButtonCallback)

}

final class UICallbackButton : UIButton, UIActionButton {

    var buttonCallback: ButtonCallback!

    func on(event : UIControlEvents, then callback: ButtonCallback) {
        buttonCallback = callback
        self.addTarget(self, action: #selector(UICallbackButton.executeCallback(_:)), forControlEvents: event)

    }

    func executeCallback (_ : UIButton) {
        buttonCallback()
    }
}