Swiftで UIButton#addTarget するActionには @IBAction が必須？？（→ではなかったけど気をつけないといけないことがあるようです）



はじめに
=====

昔からそうだったのかもしれませんが（addTarget使ったの初めてだから以前をしらない ^^;)、

Xcode6 Beta6 で UIButton などに addTarget するActionに `@IBAction` が無いとエラーになりました。

**追加検証も御覧ください**

現象
=====

```
func someMethod() {
    btnRight.addTarget(self, action: "onBtnRight:", forControlEvents: .TouchUpInside)
}

func onBtnRight(sender: AnyObject) {
   ...
}
```

と書いてたら、

```
2014-08-22 16:16:15.265 hogehoge[33698:60b] *** NSForwarding: warning: object 0x7fd9dac1d2c0 of class '_TtC6hogehoge18TileViewController' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[hogehoge.TileViewController onBtnRight:]

```

となります。 よく コロン `:` を忘れて  `action: "onBtnRight"` とやってしまったときのような症状です。

解決？
=====

謎だなーと思って調べてもよくわからず。なんとなく `@IBAction` を試しに付けてみたら、ちゃんとActionが呼ばれました。

```
func someMethod() {
    btnRight.addTarget(self, action: "onBtnRight:", forControlEvents: .TouchUpInside)
}

@IBAction func onBtnRight(sender: AnyObject) {
   ...
}
```


さいごに
=====

`@IBAction` は InterfaceBuilder 専用のMarkerだと思っていたんだけど。。。
何かの間違い（バグ）かもしれないし、昔からこうなのかもしれないですが、ちょっと意外でわからなかった…


(2014/8/23) 追加検証・１
======

@takabosoft さんからコメントを頂いたので追加検証をしてみました。
コメントで頂いたようによくある使い方だとそのような問題はおきませんでした。

結論としては、Target Objectがポイントとなるようで、 **TargetがNSObject的な性質を持っていれば問題ない** ようです。
どうも **SelectorでActionを指定するようなObjectはそうみたいです**。

再現しないコード
------

```
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        var btn = UIButton.buttonWithType(.System) as UIButton
        btn.frame = CGRectMake(0, 0, 100, 100)
        btn.setTitle("TEST", forState: .Normal)
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
        view.addSubview(btn)
    }

    func didTouchUpInside(sender: AnyObject) {
        println("called!")
    }
}
```

再現するコード
------

このようなコードだと再現しました（落ちました）。 Target Object(Hoge object)が問題みたいです。

```
import UIKit

class ViewController: UIViewController {
    var hoge: Hoge = Hoge()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        var btn = UIButton.buttonWithType(.System) as UIButton
        btn.frame = CGRectMake(0, 0, 100, 100)
        btn.setTitle("TEST", forState: .Normal)
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
        view.addSubview(btn)
        
        hoge.someMethod(btn)
    }
    
    func didTouchUpInside(sender: AnyObject) {
        println("called!")
    }
}

class Hoge {
    func someMethod(btn: UIButton) {
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
    }

    func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
}
```

### 再現しないようにするには・その１

`@IBAction` を付ける

```
class Hoge {
    func someMethod(btn: UIButton) {
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
    }

    @IBAction func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
}
```

### 再現しないようにするには・その２

Hogeが`NSObject`を継承する。

```
class Hoge : NSObject {
    func someMethod(btn: UIButton) {
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
    }

    func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
}
```

### 再現しないようにするには・その３

`@objc class Hoge` とする。

```
@objc class Hoge {
    func someMethod(btn: UIButton) {
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
    }

    func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
}
```

(2014/8/23) 追加検証・２
======

NSNotificationCenter だとどうなるのだろう、と思って２行追加しました。
やはりこれだと落ちるみたいです。ただ、上記のように `NSObject`や`@objc`を付けると落ちませんでした。`@objc`を付けるのは`class`の前でなく`func`のところでも良いようです。

Selector周りはそういう動作なのかもしれません。

```
    @objc func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
```


```
import UIKit

class ViewController: UIViewController {
    var hoge: Hoge = Hoge()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        var btn = UIButton.buttonWithType(.System) as UIButton
        btn.frame = CGRectMake(0, 0, 100, 100)
        btn.setTitle("TEST", forState: .Normal)
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)
        view.addSubview(btn)
        
        hoge.someMethod(btn)
        
		// この２行を追加
        NSNotificationCenter.defaultCenter().addObserver(hoge, selector: "didTouchUpInside:", name: "Hoge", object: nil)
        NSNotificationCenter.defaultCenter().postNotificationName("Hoge", object: self)
    }
    
    func didTouchUpInside(sender: AnyObject) {
        println("called!")
    }
}

class Hoge {
    func someMethod(btn: UIButton) {
        btn.addTarget(self, action: "didTouchUpInside:", forControlEvents: .TouchUpInside)

    }

    func didTouchUpInside(sender: AnyObject) {
        println("222222222222")
    }
}
```