ProgrammingiOS開発者

Swiftにおける動的メソッド呼び出しはどのように機能しますか?名前でメソッドを実行することを可能にするメカニズム(例えば、@objc、dynamic、AnyObject)にはどのようなものがあり、どのような条件でこれが可能になりますか?使用例を示してください。

Hintsage AIアシスタントで面接を突破

回答

動的メソッド呼び出しとは、コンパイル時ではなく、実行時に名前でメソッドを呼び出すことができる機能です。Swiftでは、@objcおよびdynamic修飾子を使用することによって、またはAnyObjectプロトコルを用いることによって、Objective-Cランタイムと連携することでこれが可能です。

メカニズム:

  • @objc — メソッド/プロパティ/クラスをObjective-Cランタイムにエクスポートするためにマークします。
  • dynamic — スタティック呼び出し(vtableを通じた呼び出し)ではなく、Objective-Cメッセージディスパッチを介して実行されることを要求します。
  • AnyObjectプロトコルはメソッドをオプショナルとして宣言し、オプショナルチェイニングを介して呼び出すことを許可します。

制約:

  • NSObjectから継承されたクラス、または@objcでマークされたプロトコルでのみ機能します。
  • @objc属性を持つメソッド/プロパティや、NSObjectからの継承を通じてマークされたメソッド/プロパティのみに適用されます。

例:

import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Selector (perform:)による動的呼び出し: if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } }

同様に、AnyObjectを使用してオプショナルチェイニングを介して関数を呼び出すこともできます:

@objc protocol Greeter { @objc optional func greet() } class Robot: NSObject, Greeter { func greet() { print("Beep-beep!") } } let anyGreeter: AnyObject = Robot() anyGreeter.greet?() // "Beep-beep!"

問題がある質問

structは@objc、dynamic、またはAnyObjectを介して動的メソッド呼び出しをサポートできますか?

回答: いいえ、NSObjectから継承されたクラス、または@objc修飾子を持つクラス/プロトコルのみがそのようなメカニズムをサポートできます。StructやenumはObjective-Cランタイムと互換性がないため、動的なメンバーにはなれません。


このテーマの微妙な点を知らないことによる実際のエラーの例


物語

プロジェクトで@objcプロトコルをデリゲートとして使用していました。開発者の一人がデリゲートクラスのNSObjectからの継承を取り除いたため、オプショナルメソッドがオプショナルチェイニングを介して呼び出せなくなりました。アーキテクチャが機能しなくなり、テストが常に失敗するようになりました。


物語

構造体のプロパティにKVO(key-value observing)を使用しようとする際に、開発者はそれをdynamicとしてマークしました。コードはコンパイルされましたが、期待通りに動作しませんでした。なぜなら、structはObjective-Cランタイムをサポートしていなかったからです。変更に対する動的な反応が機能せず、UIの更新が見逃されました。


物語

クラスの拡張でperform(_:で呼び出される関数に@objc修飾子を追加するのを忘れました。その結果、実行時に無関係なセレクタを呼び出そうとしたときにクラッシュが発生しました。原因を長い間探しましたが、関数がObjective-Cにエクスポートされていないことに気づくまでかかりました。