別のテーマに行こうと思いましたがもう少しだけ変わらない変数をテーマにしたいと思います。
変わらないインスタンス変数 in Objective-C
ローカル変数ほど頻度は高くはありませんが、インスタンス変数も初期値を設定した後インスタンスが消滅するまで値が変わらない場合があります。
ではObjective-Cのインスタンス変数にconst
をつけるとどうなるでしょうか。
@implementation Book {
NSString * const title = @"Default Title";
}
Parse Issue: Expected ';' at end of declaration list
値を設定することができません。
Objective-Cでは変わらないインスタンス変数は実現できないようです。
どうしても実現したい場合はそれに近い方法として@property
で宣言しreadonly
属性をつけるなどとして工夫するしかなさそうです。
変わらないプロパティ in Swift
Swiftのlet
ではどうでしょうか。
class Book {
let title:String = "Default Title"
}
インスタンス変数に相当するものはSwiftではプロパティとなりますがlet
をつけても値を設定することが可能です。さらに
class Book {
let title:String
init() {
self.title = "Default Title"
}
}
と値をinit()
(イニシャライザ、初期化子)の中で設定することも可能です。
Swiftのlet
の方が遥かに柔軟に値を設定することができるようですね。
依存関係をともなう初期化
Swiftで依存関係をともなうような複雑な初期化でもプロパティにlet
をつけることが可能かどうか。具体的なコードで見てみましょう。
class Author {
let bestsellerTitle:String = "Bestseller Title"
let book:Book
init() {
self.book = Book(author:self)
}
}
class Book {
let title:String = "Default Title"
init(author:Author) {
self.title = author.bestsellerTitle
}
}
クラスAuthor
はクラスBook
のインスタンスをプロパティとして保持し、init()
の中で値を設定しています。
一方クラスBook
も自分を生成したクラスAuthor
のインスタンスをinit()
の引数で受け取っています。
これをコンパイルしようとするとクラスAuthor
のinit()
でエラーになります。
Swift Compiler Error: Variable 'self.book' used before being initialized
init()
でself
(インスタンス自身)を参照しているのですが、プロパティbook
がまだ初期化されていないためエラーになったようです。
この初期化問題の解決に役立ちそうな記述がドキュメントThe Swift Programming LanguageのInitializationの節Optional Property Typesにありました。
SwiftにはOptional Type(オプショナルな型)という非常に重要な考え方があります。
オプショナルな型は通常の型と違い、値がnil
をとることを許します。
逆にいえばSwiftでは通常の型で宣言した場合、値がnil
をとることを許していません。
これはObjective-Cでは無かった区別で、プログラムがより安全になる反面、その選択には注意が必要になります。
表記方法は簡単で通常の型に対してその後ろに?
をつければ(対応する)オプショナルな型になります。
オプショナルな型を使って先ほどのコードを書き直してみましょう。
class Author {
let bestsellerTitle:String = "Bestseller Title"
let book:Book?
init() {
self.book = Book(author:self)
}
}
class Book {
let title:String = "Default Title"
init(author:Author) {
self.title = author.bestsellerTitle
}
}
書き直すといってもlet book:Book
をlet book:Book?
にしただけですが、これによりクラスAuthor
のinit()
でのコンパイルエラーが無くなりました。
何もしなくてもプロパティbook
にはnil
が設定されるようになったからです。
しかしこのままだと少し不便なことがあります。
プロパティbook
をオプショナルな型で宣言してしまったがために、その後book
を参照する度に値がnil
かどうか条件判定するはめになります。
book
はlet
がついていてかつinit()
で値を設定していますのでnil
にはなることは無く、条件判定は全く不要です。
Swiftはこのように文法上はオプショナルな型であることを必要とするものの、処理上はオプショナルな型であることを不要とする場合、?
の代わりに!
を用いてオプショナルな型を宣言することができます(この型は無条件で開封されるオプショナルな型(Implicitly Unwrapped Optional Type)と呼ぶようです)。
これでわずらわしい条件判定を意識しないで参照できるようになります。
念のため再度書き直してみましょう。
class Author {
let bestsellerTitle:String = "Bestseller Title"
let book:Book!
init() {
self.book = Book(author:self)
}
}
class Book {
let title:String = "Default Title"
init(author:Author) {
self.title = author.bestsellerTitle
}
}
これでOKです。
変わらない変数の話は終わりにして次回はやっと別のテーマに移ろうと思います。 ではでは。