定食屋おろポン

おろしポン酢と青ネギはかけ放題です

SwiftでNSTimerとかUIButtonを使うときはBlocksKit使うといいよ

Cocoaでは、引数にtargetselectorを取るメソッドがいくつかありますね。

  • NSTimer+ scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
  • UIControl- addTarget:action:forControlEvents:

こういうメソッドを、純粋なSwiftのクラス--つまり、NSObjectを継承していないクラス--で使おうとすると、クラッシュを引き起こします。*1

実際にこのコードは実行時にクラッシュします。

class SwiftClass {
    init() {
        NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("privateFunc"), userInfo: nil, repeats: true)
    }
    
    private func privateFunc() {
        println("hello")
    }
}

これを回避するには、単純にNSObjectから継承すれば済みます。 もう一つの手段としては、BlocksKitを使うことです。

  1. CocoaPodsなどでBlocksKitをプロジェクトに組み込む
  2. Objective-Cで実装されたBlocksKitをSwiftから使うために、ブリッジングヘッダ(<#Project-name#>-Bridging-Header.h)でBlokcsKitのヘッダを読み込む
  3. BlocksKitが提供しているAPIを使う

コードで示します。これはクラッシュせずに動作し、SwiftClassがNSObjectを継承せずにNSTimerを使用できていることがわかります。

class SwiftClass {
    init() {
        NSTimer.bk_scheduledTimerWithTimeInterval(1.0, block: { (t) -> Void in
            println("hello")
        }, repeats: true)
    }
}

BlocksKitを使用することで、コールバック関数を設定するコードとコールバック関数を同じ場所に書けますので、関心を集約することができます。 SwiftプロジェクトではBlocksKitの使用をオススメします。

違う話になるため細かい記述は避けますが、ブロックを使用する際に気をつけないと循環参照を引き起こすので、そこだけは気をつけましょう。

CocoaDocs.org - BlocksKit Reference

*1:レシーバオブジェクトとセレクタからメソッドを呼び出す際に、NSObjectに実装されているメソッドに依存しているためです。また、正確にいうと「NSOjbectを継承していないオブジェクトをターゲットに渡すと」クラッシュします。