そして最近、また新しい言語、Swift を触りはじめた訳なんですが、C# と Java はまだ、基本構文が C と一緒なのでほとんど混乱もなかったのですが、Swift は一見似てるようでだいぶ違うので、慣れるまで結構大変でしたね。
てことで、せっかくだし、ハマった罠や、ちょっといいなと思った事くらいはメモしとく事にしましょう。小ネタばっかなので、溜まるたびにたまにまとめて書く事にします。
ちなみに、基本的な事はほぼ以下のページで学ばせていただきました。文量もそんな多くないし、C# や Java を経験してる人なら一気に読めば大方把握できるんじゃないかと思います。
switch 大躍進!
Swift の構文をひととおり眺めてたら、なんか switch がやたら高機能化されてて笑った。どんだけ switch 好きなんだよと。switch ( a ) { case 1: // {} で囲まなくても、case 内はローカルスコープ扱い 処理 // ←break がなくてもここで終わる case 2: 処理 fallthrough // 下の case に続行したい場合は明示的にこう書く case 3, 4: // 複数の場合は、case を並べるのではなく、カンマで並べる 処理 case b: // なんと変数もアリ!。結果が被った場合は、上の物が勝つ 処理 default: // 何もしなくても default 必須なのはウザイ break // 何も処理しない場合のみ break 必須 }
上記の他にも、範囲指定とかまで書けたり、さらに予想外な記法もあるみたいなので、以下とか読んでみるといろいろ役に立つかも。
あとそういや、Swift には if での範囲判定が1文で書ける構文 があるらしく、それはちょっといいねと一瞬心躍ったのですが、
if (value >= 10) && (value <= 20) // ↓これがこう書ける。Swift ならね! if 10...20 ~= value
あのゴメン。何したいのか全然わかんない
...
だけでだいたい分かるのに、~=
の意図が全然分からん。どこの文化だ?
配列がなんかキモイんですけど
元はといえば Swift ではオブジェクトの clone はどう実装するのかとかを調べてたんですが、なんか恐ろしい記事を見つけてしまった。 と、Swift では配列は参照型ではなく値型で、引数渡しも含め、他の変数に代入しただけでディープコピーのように振舞う みたいですね。まあ実際には、配列が変化した時点で初めてディープコピーされる感じみたいですけど、裏でコソコソそういうのされるのキモイわー。上の記事では一生懸命いろいろ書いてくださってましたが、どう考えても罠以上のメリットがあるとは思えないけどなぁ。数ある言語の中で敢えてごく少数派の、しかもコンピュータの仕組み的に非合理な挙動をする事に何の意味があるんか。まあ慣れるしかないんだけどさ。
あとそうだ、もひとつ Swift の配列のキモイポイントといったら、配列を指定数だけ確保するのがやたらめんどい んですよ。何を言ってるのか分か(以下略)
// サイズ10の配列を確保するのにイチイチこれ書かないといけない var array = [Int]( repeating: 0, count: 10 ) // ちなみに、Swift2 まではこう。心底どっちでもいいし変えんな! var array = [Int]( count: 10, repeatedValue: 0 )
// これだと、配列全部に同じインスタンスが入る。何に使うんだよソレ... var array = [MyClass]( repeating: MyClass(), count: 10 ) // とりあえず nil で確保しようとしても、Swift のポリシー的に不可 // オプショナルでもいいけど、直後に全部入れる前提なら無駄すぎ var array = [MyClass!]( repeating: nil, count: 10 ) var array = [MyClass?]( repeating: nil, count: 10 ) // 結局こうなのかなぁ。なんか2度手間でイヤ var array = [MyClass]( repeating: MyClass(), count: 10 ) for i in 0..<array.count { array[i] = MyClass() } // もしくはこう?。すげえ遅そう var array = [MyClass]() for _ in 0..<10 { array.append( MyClass() ) }
repeating:
で別のインスタンス入れてくれるとありがたいんだけどなぁ。C# や Java もだけど、オブジェクトの配列を作るために for 回すの、泥臭くてキライ。
protocol にデフォルト実装できますやん
Java から初めて interface を駆使しするようになったんですが (※C# の頃は interface より delegate ばっか使ってた)、interface って必ず全メソッドを実装しないといけないのがとても面倒でイヤ だったんですよね。で、Swift にも interface と同等の概念、protocol ってのがあるんですが、でもまあこれも当然全メソッドを実装しないといけないのでガッカリしてたのですが、UITableView を使おうとした時に、protocol のメソッドを全て実装しなくても動いてる事に気づきました。えっ何これ?
そんで調べてみたところ、やり方が2種類あって、
- Objective-C には optional という概念があって、必要ないメソッドは省略できたのだが、Swift でもクラスを Objective-C 互換にすれば、それを無理矢理使える。ただし、メソッドを呼ぶ前に実装済みかのチェックが必要になるし、Swift の機能に制約がかかるらしい。
- protocol の extension を作れば、デフォルト実装を書ける!
static class は enum にオマカセ
アプリ全体で共有する定数や、便利関数だけを寄せ集めたユーティリティクラスとか、インスタンスを作る必要が全くないクラス の場合、c# ではstatic class
ってすればインスタンスが作れなくなって、ちょっぴり清々しかったのですが、Swift にはそんな機能はありません。まあそもそもチーム開発でもないし、そんな事気にする必要もないのですが、Swift では代わりにこんな書き方ができるそうな。
enum AppUtil { // 共通の定数詰め合わせとか static let ConstValue1 : Int = 10 static let ConstValue2 : Int = 10 // 共通の便利機能詰め合わせとか static func utilFunction1() { なにがし } static func utilFunction2() { なにがし } }
あと enum といえば、Swift といい Java といい、なんで無駄に enum を高機能化したがるんですかね。皆はアレ便利便利言うけど、コレなんか無駄にインスタンス作ってね?とか、Int から変換時にいちいち全検索してね?とか疑心暗鬼になって、どうにも素直に使えん。
こっちとしては、enum は int の連番定数を楽に作れるだけの機能でいいんだけどなぁ。個数管理するのにも重宝してたし。
無名のスコープはダメゼッタイ
嫌悪する人もいるかもしれませんが、僕は初期化メソッドとかが肥大化しちゃった時とかに、よくこんな書き方をします。func initialize() { // ある機能の初期化 { : } // 他の機能の初期化 { : } }
でも Swift だと、無名のスコープを作るとエラーなんですよね。なんでやねんと思ったら、こう書くんだそうです。
do { ... }
repeat { ... } while( 条件式 )
ちなみに、無名スコープがエラーだと実際に知る前からイヤな予感はしてました。Swift では、関数の引数の末尾がクロージャの場合、クロージャをメソッドの外に書ける んですが、
// こんな風に末尾がクロージャのメソッドは func myFunction( param: Int, callback: (Void) -> Int ) // 正式な呼び方はこうだけど myFunction( param: 0, callback: { return 10 } ) // こう書いても同じ意味になる myFunction( param: 0 ) { return 10 } // もし引数がクロージャのみの場合は、()まで省略可能 // もう関数なのかすらよく分かんねえ myFunction { return 10 }
そういやまた余談ですが、 Swift のクロージャの引数って、なんであんな不気味な記法になったんですかね。なんで中括弧の中に引数書くんだよ。in てなんだよ。
C スタイルの for やめましたって言うけどさ
Swift3 で、前から非推奨だった C スタイルの for 文がとうとう廃止されましたね。// Cスタイルの記法は Swift3 で廃止 for var i = 0; i < 10; i++ // Swift では本来こう書く // なんでか ..< の左右にスペース入れるとエラーなのがムカツク for i in 0..<10
ここだけC
int i = 0, count = 10; for ( ; i < count; i++ ) { // なにかあったら途中で止める if ( ... ) break; } // 続きは他の処理をする for ( ; i < count; i++ ) { ... }
var i = 0 while ( i < 10 ) { // このスコープから出る時、以下が呼ばれる defer { i+= 1 } // あとは途中で continue でも何でもやり放題ヨロレイヒー }
コレ面白いのが、スコープから出る時呼ばれるっても、defer 文より上で return とかで抜けた場合には呼ばれない ようにできてるんですよね。器用な作りだなぁ。
という事で、for の愚痴と見せかけて、残念、実は defer のステマでした。 なお、ホントに上の例みたいなコードを移植するとしたら、よほど急いでるでもなければ、ちゃんと Swift として読みやすく書き直しますよ。最悪
defer { i += 1 }
という手もあるということで。