質問: Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(SelectorTestViewController.count), userInfo: nil, repeats: true) } // internalだと@objcは必要なかった @objc fileprivate func count() { 処理 } } ``` 上記の様な実装をした際に幾つか疑問が生じました。 -`selector`で指定した関数のアクセスレベルが`internal`だと`@objc`が要らず、`fileprivate`以下だと必要になるのはどういう原理なのか -そもそもなんで`@objc`が必要なのか この辺りの動作を(直接又はURLへの誘導等で)ご教授頂けると幸いです。
回答: 呼び出し方式 ======= Swift でメソッドを呼び出す方法(仮想テーブルによる方法または直接実行)と Objective-C でメソッドを呼び出す方法(メッセージパッシング方式)は、内部では違う方法がとられます。 Selector は Objective-C 方式の呼び出し方になります。 Objective-C 方式で呼び出せるメソッドは Objective-C 方式として定義されたメソッドだけになります。つまり Selector では `@objc` メソッドしか呼び出せません。Selector で `@nonobjc` メソッドを呼び出そうとすると、メソッドが見つからず、実行時エラーになります。 今回の問題 ====== Objective-C 互換クラスでメソッドを定義するとき、原則として * Swift 式の呼び出しをするメソッドには `@nonobjc` をつけて定義し、 * Objective-C 式の呼び出しをするメソッドには `@objc` をつけて定義しますが、 普通は `NSObject` を継承したクラスの `func` には自動で `@objc` がつけられて、そうではない純粋な Swift クラスのときは `@nonobjc` がつけられます。 ただし、アクセスレベルが `fileprivate` 以下の場合、既定では `@nonobjc` が指定されたことになるようです。きっと "アクセス範囲がその Swift ファイルを超えない"、つまり "Objective-C から呼べる必要がない" みたいに解釈されているのかもしれません。 この違いから、`internal` なら `@objc` 方式だったため Selector で呼び出せますが、`fileprivate` だと `@nonobjc` 方式になってしまうため、明示的に `@objc` を明記しないと Selector で呼び出せなくなるようです。 おまけ ==== アクセスコントロールを `internal` にして、それに `@nonobjc` をつけると、`fileprivate` のときみたいに Selector で呼び出せなくなります。