Swift のクラスとプロトコルの書き方及び Objective-C の動作
相互運用性により、Objective-C の動作を組み込んだ Swift コードを書くことができます。Objective-C クラスをサブクラス化し、Objective-C プロトコルを宣言して採用し、Swift コードを書くときに Objective-C の他の機能を利用できます。これは、Objective-C のよく知られた、確立された動作に基づいてクラスとプロトコルを作成し、Swift の現代的で強力な言語機能でそれらを強化できることを意味します。
Objective-C のクラスからの継承
Swift では、Objective-C クラスのサブクラスを定義できます。Objective-C クラスから継承した Swift のクラスを作成するには、コロン(:) を Swift のクラスの名前の後に続け、その後に Objective-C クラスの名前を続けます。
- import UIKit
- class MySwiftViewController: UIViewController {
-        // define the class
- }
<< SWIFT >>
Swift のサブクラスは、Objective-C のスーパークラスによって提供されるすべての機能を取得します。
スーパークラスのメソッドの独自の実装を提供するには、override 修飾子を使用します。コンパイラは、Swift のメソッド名と一致する、オーバーライドされた Objective-C メソッドの名前を自動的に推測します。 @objc(name) 属性を使用して、対応する Objective-C シンボルを明示的に指定できます。
Swift クラスが Objective-C 実行環境からの動作を必要とする多くの新しいメソッドまたはプロパティを導入する場合、そのクラスの宣言で @objcMembers 属性を使用します。@objcMembers 属性をクラスに適用すると、@objc 属性がすべての Objective-C 互換メンバに暗黙的に追加されます。と言うのも、@objc 属性を適用すると、アプリのコンパイルされたサイズが増え、パフォーマンスに悪影響を与える可能性があるため、各メンバーが @objc 属性を適用する必要がある場合にのみ宣言に @objcMembers 属性を適用します。
NSCoding
NSCoding プロトコルでは、準拠型が必須イニシャライザ init(coder:) と必須メソッド encode(with:) を実装する必要があります。NSCoding を直接採用するクラスでは、このメソッドを実装しなければなりません。1つ以上のカスタムイニシャライザまたは初期値のない全てのプロパティを持つ NSCoding を採用するクラスのサブクラスでも、このメソッドを実装しなければなりません。
ストーリーボードからロードされたり、NSUserDefaults クラスまたは NSKeyedArchiver クラスを使用してディスクにアーカイブされたオブジェクトの場合、このイニシャライザの完全な実装を提供しなければなりません。ただし、このようにしてインスタンス化することが予想される、またはインスタンス化できない型のイニシャライザを実装する必要はありません。
プロトコルの採用
Objective-C プロトコルは Swift プロトコルとして import され、それは、もしあればクラスのスーパークラスの名前が続く、コンマで区切られたリストの中でクラスによって採用されます。
- class MySwiftViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource { -        // define the class
- }
<< SWIFT >>
Swift のコードで単一のプロトコルに準拠する型を宣言するには、その型として直接プロトコル名を使用します(Objective-C での id<SomeProtocol> と比較して)。Swift コードで複数のプロトコルに準拠する型を宣言するには、SomeProtocol & AnotherProtocol(Objective-C の id<SomeProtocol,AnotherProtocol> と比較して) という形式から取ったプロトコルコンポジションを使用します。
- var textFieldDelegate: UITextFieldDelegate
- var tableViewController: UITableViewDataSource & UITableViewDelegate
<< SWIFT >>
Objective-C プロトコルの要件を満たすために Swift のイニシャライザ、プロパティ、サブスクリプト、またはメソッドを使用すると、コンパイラはオーバーライドされたメソッドの場合と同様に、要件に一致する名前を自動的に推測します。@objc(name) 属性を、対応する Objective-C シンボルを明示的に指定するために使用できます。
イニシャライザとデイニシャライザを書く
Swift コンパイラは、イニシャライザが初期化されていないクラスのプロパティをそのままにしないようにして、コードの安全性と予測可能性を向上させます。さらに、Objective-C とは異なり、Swift では、呼び出すべき個別のメモリ割り当てメソッドがありません。Objective-C クラスを使用している場合でも、Swift の固有の初期化構文を使用できます。Swift は、Objective-C 初期化メソッドを Swift イニシャライザに変換します。独自のイニシャライザの実装の詳細については、Swift プログラミング言語(Swift 4.0.3) の イニシャライザを参照してください。
クラスの割り当てを解除する前に追加のクリーンアップを実行したい場合は、dealloc メソッドの代わりにデイニシャライザを実装できます。Swift のデイニシャライザは、インスタンスの割り当て解除が起こる直前に自動的に呼び出されます。Swift は、サブクラスのデイニシャライザを呼び出した後、スーパークラスのデイニシャライザを自動的に呼び出します。Objective-C クラスを使って作業しているとき、または Swift クラスが Objective-C クラスから継承しているとき、Swift はクラスのスーパークラスの dealloc メソッドをさらに呼び出します。独自のデイニシャライザの実装の詳細については、Swift プログラミング言語(Swift 4.0.3) の デイニシャライザ を参照してください。
Objective-C API で Swift クラス名を使用する
Swift クラスは、Objective-C コードから使用した場合でも、コンパイルされたモジュールに基づいて名前空間を持ちます。すべてのクラスがグローバルの名前空間の一部であり、同じ名前であってはならない Objective-C とは異なり、Swift クラスは、それらが常駐するモジュールに基づいて曖昧さをなくすことができます。たとえば、MyFramework という名前のフレームワーク内の DataManager という名前の Swift クラスの完全修飾名は MyFramework.DataManager です。Swift アプリのターゲットはモジュール自体なので、MyGreatApp という名前のアプリ内の Observer という名前の Swift クラスの完全修飾名は MyGreatApp.Observer です。
Swift のクラスが Objective-C コードで使用されているときに、名前空間を保持するために、Swift クラスは完全修飾名で Objective-C 実行時環境に公開されます。したがって、Swift クラスの文字列表現で動作する API を操作する場合は、クラスの完全修飾名を含めなければなりません。たとえば、ドキュメントベースの Mac アプリを作成する場合、アプリの Info.plist ファイルに NSDocument サブクラスの名前を提供して下さい。Swift では、アプリまたはフレームワークの名前から派生したモジュール名を含め、ドキュメント・サブクラスのフルネームを使用しなければなりません。
以下の例では、NSClassFromString(_:) 関数を使用して、その文字列表現からクラスへの参照を取得します。Swift クラスを取得するには、アプリの名前を含む完全修飾名が使用されます。
<< SWIFT >>
let myPersonClass: AnyClass? = NSClassFromString("MyGreatApp.Person")
Interface Builder との統合
Swift のコンパイラは Swift クラス用の Interface Builder の機能を有効にする属性を含んでいます。Objective-C の場合と同様に、Swift でアウトレット、アクション、およびライブレンダリングを使用できます。
アウトレットとアクションでの作業
アウトレットとアクションを使用すると、Interface Builder でユーザインタフェース・オブジェクトにソースコードを接続することができます。Swift でアウトレットとアクションを使用するには、プロパティまたはメソッド宣言の直前に @IBOutlet または @IBAction を挿入します。アウトレット・コレクションを宣言するには、同じ @IBOutlet 属性を使用し、型の配列を指定するだけです。
Swift でアウトレットを宣言するときにはアウトレットの型を、暗黙に開封された optional にする必要があります。このようにして、ストーリーボードは実行時に、初期化した後に、アウトレットを接続させることができます。あなたのクラスが、ストーリーボードや xib ファイルから初期化されるときは、アウトレットが接続されていると仮定できます。
たとえば、以下の Swift コードはアウトレット、アウトレット・コレクション、およびアクションを持つクラスを宣言します。
- class MyViewController: UIViewController {
-        @IBOutlet weak var button: UIButton!
-        @IBOutlet var textFields: [UITextField]!
-        @IBAction func buttonTapped(_ sender: UIButton?) {
-                print("button tapped!")
-        }
- }
<< SWIFT >>
ライブレンダリング
Interface Builder でライブな、インタラクティブなカスタムビューの設計を可能にするために @IBInspectable と @IBDesignable の2つの異なる属性を使用できます。UIView クラスまたは NSView クラスから継承するカスタムビューを作成すると、クラス宣言の直前に @IBDesignable 属性を追加することができます。(インスペクタ·ペイン内のビューのカスタムクラスを設定することで) Interface Builder にカスタムビューを追加した後、Interface Builder はキャンバス内にビューをレンダリングします。
また、ユーザー定義した実行時属性と互換性のある型とプロパティに @IBInspectable 属性を追加することもできます。Interface Builder にカスタムビューを追加した後は、インスペクタでこれらのプロパティを編集できます。
- @IBDesignable
- class MyCustomView: UIView {
-        @IBInspectable var textColor: UIColor
-        @IBInspectable var iconHeight: CGFloat
-        /* ... */
- }
<< SWIFT >>
プロパティ属性の指定
Objective-C ではプロパティは、プロパティの動作についての追加情報を指定する潜在的な属性の範囲を持っています。Swift では、別の方法でこれらのプロパティ属性を指定します。
Strong と Weak
Swift のプロパティは、デフォルトでは strong (強い) です。プロパティがその値として格納されたオブジェクトへの弱い参照を持つことを示すために、weak のキーワードを使用します。このキーワードは、optional のクラス型であるプロパティのためにだけ使用できます。詳細については、属性 を参照してください。
読み/書き可能と読み取り専用
Swift では、readwrite や readonly 属性はありません。格納されたプロパティを宣言する場合、let を使うと読み取り専用になり、var を使うと読み/書き可能になります。計算されたプロパティを宣言する場合、getter のみを提供すると読み取り専用になり、getter と setter 両方を提供すると読み/書き可能になります。詳細については、Swift プログラミング言語(Swift 4.0.3) の プロパティ を参照してください。
コピーの意味
Swift では、Objective-C の copy プロパティ属性は @NSCopying に変換されます。プロパティの型は NSCopying プロトコルに準拠していなければなりません。詳細については、Swift プログラミング言語(Swift 4.0.3) の 属性 を参照してください。
コアデータ管理オブジェクトサブクラスの実装
コアデータは NSManagedObject クラスのサブクラス内のプロパティの基本的な格納および実装を提供します。コアデータはまた、対多の関係からオブジェクトを追加または削除するために使用するインスタンスメソッドの実装も提供しています。@NSManaged 属性を使用して、コアデータが実行時に宣言の格納と実装を提供することを Swift コンパイラに通知して下さい。
コアデータモデルの属性または関係に対応する、管理オブジェクトサブクラス内の各プロパティまたはメソッド宣言に @NSManaged 属性を追加して下さい。例えば、文字列属性 "name" と多対多関係 "friends" を持つ "Person" というコアデータの実体を考えてみましょう。
NSManagedObject サブクラス、Person の対応する Swift コードを生成するために、"Editor" メニューから "Create NSManagedObject Subclass ..." を選択してください。
- // Person+CoreDataClass.swift
- import CoreData
- class Person: NSManagedObject {
-         // Insert code here to add functionality to your managed object subclass
- }
- // Person+CoreDataProperties.swift
- extension Person {
-         @NSManaged var name: String
-         @NSManaged var friends: NSSet
- }
<< SWIFT >>
name と friends プロパティはどちらも @NSManaged 属性で宣言され、コアデータが実行時にその実装と格納を提供することを示します。
コアデータモデルの実体で使用する NSManagedObject の Swift のサブクラスを構成するには、Xcode でモデル実体インスペクタ(model entity inspector) を開き、Class フィールドにクラス名を入力し、Module フィールドのドロップダウンリストから"現在の製品モジュール"(Current Product Module) を選択して下さい。
プロトコルの宣言
Swift では、Objective-C クラスが準拠できるプロトコルを定義できます。Objective-C クラスが採用できる Swift プロトコルを作成するには、protocol 宣言を @objc 属性でマークします。
- import UIKit
- @objc protocol MyCustomProtocol {
-         var people: [Person] { get }
-         func tableView(_ tableView: UITableView, configure cell: UITableViewCell,
forPerson person: Person) -         @objc optional func tableView(_ tableView: UITableView, willDisplay cell:
UITableViewCell, forPerson person: Person) - }
<< SWIFT >>
プロトコルは、Objective-C クラスがプロトコルに準拠するために実装しなければならないすべてのイニシャライザ、プロパティ、サブスクリプト、およびメソッドを宣言します。オプションのプロトコル要件は、@objc 属性でマークし、optional 修飾子を付けなければなりません。
Objective-C クラスは、必要なメソッドを実装することによって、Objective-C プロトコルと同じ方法で Swift で宣言されたプロトコルに準拠できます。
- @interface MyCustomController: UIViewController <MyCustomProtocol>
- @property (nonatomic, strong) NSArray<Person *> *people;
- @end
- @implementation MyCustomController
- @synthesize people;
- - (void)tableView:(UITableView *)tableView
-                 configure:(UITableViewCell *)cell
-                 forPerson:(Person *)person
- {
-       // Configure cell
- }
- @end
前:Objective-C の API との相互作用 次:Cocoa フレームワークでの作業