概要
普段何気なく使用しているNSObjectについて、改めて調べまとめました。
なお、筆者はSwift、Objective-C及びアプリ開発初心者のため、内容の正確性をしっかり保証することができません。もし、不正確な表現などございましたら、コメントいただけると幸いです。
NSObjectとは
Cocoaフレームワークにおける数少ないルートクラスで、NSObjectを継承することで、動的型付けであるObj-Cランタイムの柔軟性を教授することができます。
その柔軟性とは?
メソッド呼び出しをランタイム時に動的に行えます。
より詳細には、NSObjectを継承するサブクラスのメソッド呼び出し時には objc_msgSend()
によるインターセプションを介するため、ランタイム時にメソッド呼び出し先を柔軟に変えること(通称Swizzling)などが可能になります。
たとえば以下の一行目のようなコードはコンパイラによって、 objc_msgSend
の呼び出しに置き換えられます。
[obj message];
// 👇こうなる
objc_msgSend(obj, @selector(message));
ただし、こうした柔軟性はObj-Cが動的型付け言語故行えることであり、Swiftランタイムではコンパイル時に静的に型が決まる必要があるため、こういった柔軟で動的な挙動を得るためにはNSObject、すなわちObj-Cランタイムの力を借りる必要が出てきます。
例えば、我々がよく使うIBOutletなどのKeyValueコーディングはまさにこの機能を使用していることになります。(UIViewはNSObjectですしおすし)SwiftはObj-Cランタイムという巨人の方に乗っかっていると言えますね。
NSObjectを継承しないクラスについて
では、NSObjectを継承していないクラスの場合はどうなんでしょうか?
結論、NSObjectと継承しないSwift上のクラスは、 メソッド呼び出し時に前述の objc_msgSend()
を介しません。
ただし、NSObjectを継承していなくても、そのクラスはObj-Cクラスであり、NSObjectとの互換性を持った一部の関数の実装のみ行っているようです。また、Obj-Cランタイムメタデータもランタイム側から提供されないようです。
動的なメソッド呼び出しに関連する@objcとdynamicについて
@objc
はSwift上の実装をObj-Cから参照できるようにする
Obj-C上からSwiftで書かれたクラスのメソッドやプロパティにアクセスするためには @objc
アトリビュートが必要になります。#selectorで呼び出す場合、必ず呼び出し先に @objc
を付けることになりますが、これもObj-Cランタイムの動的なメソッド呼び出しを活用するためです。
dynamic
が付いた関数やプロパティの呼び出しには、Dynamic dispatchを使用することをコンパイラに知らせる
Apply this modifier to any member of a class that can be represented by Objective-C. When you mark a member declaration with the
dynamic
modifier, access to that member is always dynamically dispatched using the Objective-C runtime. Access to that member is never inlined or devirtualized by the compiler.
Because declarations marked with the
dynamic
modifier are dispatched using the Objective-C runtime, they must be marked with theobjc
attribute.
dynamic
キーワードを付けることで、その関数やプロパティへのアクセスは、必ずDinamyc Dispatchを介することになります。そのため、パフォーマンスの観点で悪影響があることから、使用時にはそういった観点を持ったほうが良いかも知れません。
参考リンク
What is NSObject? When do Swift developers need to use NSObject?
To the depth of @objc and dynamic in Swift
What Does the Dynamic Keyword Mean in Swift 3
mikeash.com: Friday Q&A 2012-11-16: Let's Build objc_msgSend