EDIT: Swift 2.2 ではこの問題は解決していますので、記事を参考にする際はご留意ください。2016.5
Failable Initializers
Swift の Failable Initializers について調べていたら、 以下のような記述が出ていた。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
Playground で試してみる確かにエラーにならない。しかし、struct を class に変えると…
class Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil } // error
self.species = species
}
}
なんと「全てを初期化しないと nil を返して戻れないよ」と言われてしまった。
All stored properties of a class must be initialized before returning nil from an initializer.
ドキュメントもう少し下にスクロールすると、あった! class の場合の記述。
Failable Initializers for Classes
A failable initializer for a value type (that is, a structure or enumeration) can trigger an initialization failure at any point within its initializer implementation. In the Animal structure example above, the initializer triggers an initialization failure at the very start of its implementation, before the species property has been set.
For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place.
The example below shows how you can use an implicitly unwrapped optional property to satisfy this requirement within a failable class initializer:
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
やっぱり、class の場合は、全てを初期化してからでないとダメだってさ。チェックする項目が多くて、if 文がいくらか必要な場合はそれ毎に、無意味な値に初期値を入れて、nil を返す必要があります。
例えば、PDFを扱うクラスを例に挙げると、PDFファイルのパスを与えると、最低限そのファイルがPDFとして開ける事ができるかどうかのチェックを行うと、チェックの都度 else で値を初期化して nil を返す必要があります。
```swift
class PDFContents {
let filePath: String
let numberOfPages: Int
init?(filePath: String) {
if NSFileManager.defaultManager().fileExistsAtPath(filePath) {
if let document = CGPDFDocumentCreateWithURL(NSURL.fileURLWithPath(filePath)) {
self.filePath = filePath
self.numberOfPages = CGPDFDocumentGetNumberOfPages(document)
}
else {
self.filePath = filePath
self.numberOfPages = 0
return nil
}
}
else {
self.filePath = filePath;
self.numberOfPages = 0;
return nil
}
}
}
まぁ、この場合はファイルの有無を確認を省いて、PDFを開いてみれば if 文を減らせるかもしれませんが、チェックが多い場合には不便です。何か良い記法がないものでしょうかねぇ?そして、将来のSwiftのバージョンのどこかでは、nil を返すときは初期化していない変数があっても見逃してくれるように期待したいものです。
【環境】
Xcode7.2
Swift 2