Edited at

一度Swiftを使ったら二度とObjective Cに戻れない五つの理由

More than 5 years have passed since last update.


delegateメソッドのtypoがcompile時にすぐわかる

Swiftではoverride keywordが導入されて、class methodのoverrideを明示的に示すようになった。というか、super classのmethodをoverrideするときは必ずoverrideと書かないといけない。

Objective Cでは、methodが基底クラスのoverrideなのか、それとも単にlocalで定義されているmethodなのか、コードを見ただけでは判別する手段はなかった。

optionalなdelegateを実装しているはずなのに、全然呼ばれないのでよくよく調べてみるとtypoだったという経験が必ず皆さんおありと思うが、Swiftでは基底クラスで未定義のmethodのoverrideはcompile時にerrorになるので、そんな問題はもう発生しなくなる。

コードの見通しも、ローカルなメソッドと基底クラス派生のメソッドの区別がoverrideキーワードですぐ判別できるので、#pragmaでわざわざ書いていた内容の一部は必要なくなるだろう。override keywordの追加は地味な変更のように見えるが、これに慣れると二度とObjective Cには戻れない。


Headerファイルをいちいちimportしなくてもいい

そもそもSwiftにはHeaderファイルという概念がないが、同じpackageにあるクラスや定義はimportしなくてすぐに使えるようになっている。またファイル名とそこで記述されるClass名が一致している必要もない。Swiftには結構思い切ったデザインだなと思うところがいくつかあるが、自動importもその一つじゃないだろうか。

普通の言語設計の感覚からは、自動importしてしまうとスケーラビリティや名前衝突の問題が発生するので、あまり取らない選択肢だと思うが、Swiftはアプリケーション記述言語だとすっぱり割り切っているためか、こうした判断がなされたのだと思う。

その代償として、他の言語にあるような、特定のフレームワークやパッケージの名前空間を、自由に現在の名前空間の一部にimportするといった機能は持っていない。

これは議論の分かれるところだと思うが、既存のフレームワークをそのまま活用でき、開発者の記述の負荷を軽減することを、言語仕様としての一貫性よりも取ったというのは、絶妙なトレードオフじゃないかと思う。


型推論とOptionalsおよびOptional Chainingでコードが簡潔になる

Objective Cだと、どうしてもC言語由来の型宣言形式の制約から、一行に同じ型が2回登場することがよくあった。たとえば、こうした記述はObjective Cのコードで頻繁に目にするが、

UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame: frame];

Swiftだと、型推論により、

var scrollView = UIScrollView(frame: frame)

と書ける。またよくあるnullチェックも

if (self.delegate != nil) {

if ([self.delegate respondsToSelector(reload)) {
[self.delegate reload];
}
}

Optionalsを使うと、

if self.delegate?.respondsToSelector("reload") {

self.delegate?.reload()
}

このへんはもう、

self.delegate?.reload()

でもいいかもしれない。このようにSwiftでは型宣言での冗長性を省くことができ、Optionalsではnullチェックの冗長性を省ける。いったんSwiftで書き始めるとこのコードの読みやすさの点でもObjective Cには戻れないだろう。


Closureの記述がまともになった

Objective CのClosureの記法は悪名高かったが、SwiftではまともなClosureの記述ができるようになった。これはまぁ仕方がないというか、Objective Cでは、C言語に後付けで無理やりClosureの記述を追加したので、どうしても記法が複雑になってしまうのは避けられなかったと言えるだろう。

Swiftでは記法が整理されただけではなく、関数はFirst-class objectとして定義されていて、値に代入することもできるし、引数として渡すことも可能になっている。また戻り値をタプルで戻すことができるし、引数をShorthand Argumentの仕組みで$0$1で参照することもできる。Scalaで実装してObjective Cの環境に移った時の残念感をもう味合わなくてもよくなるのだ。


Enum最高!

Enumがほとんど別物に生まれ変わっている。たとえばメソッドをEnumに定義できるので、様々なロジックをEnum内で実装することが可能だ。

たとえば、デバッグやロギングの際に、Enumの値を文字列にマップしたいことがよくあるが、従来だとEnum内にmethodを記述できなかったため、global関数を定義して、その関数への引数としてEnumの値を渡すしかなかった。

だが、SwiftのEnumとextensionの仕組みを使うと、このように既存のEnum定義にmethodを後から追加できる。

extension CLAuthorizationStatus {

func str() -> String {
switch self {
case .NotDetermined:
return "NotDetermined"
case .Restricted:
return "Restricted"
case .Denied:
return "Denied"
case .Authorized:
return "Authorized"
}
}
}

var status = CLAuthorizationStatus.Authorized
status.str()

こうすることで、globalな名前空間を汚さずに、既存のEnumへ新たな機能を追加できる。また自動importの仕組みにより、この実装がプロジェクト内のどこかにありさせすれば、どこからでもオブジクトのmethodとして呼び出すことができる。この環境に慣れるともう元には戻れないだろう。