【swift】Optional Bindingについて
前回swiftでのnil
の取り扱いについてまとめました。
今回はnil
を取り扱う時に便利な記法のOptional Bindingについてまとめます。
Optional Binding
必要になるケース
例えば、関数の中には返り値がOptionalなものがあります。
配列からindexの値を取得するfind
などがそうです。
var array: [String] = ["a", "b", "c"] find(array, 'c') // 2 find(array, 'd') // nil
そこで、find
の返り値を定数に代入してみます。
let index: NSNumber = find(array, 'd') // nil
これはコンパイルエラーになります。
定数index
はoptionalな定数ではないので、nil
は代入できません。
let index: NSNumber? = find(array, 'd') // nil
Optionalにすればnil
を代入できるようになるので、コンパイルが通るようになります。
次は、このindex
の値をもとに配列から要素を削除してみます。
array.removeAtIndex(index)
これはコンパイルエラーになります。
index
はオプショナルな定数なので!
をつける必要があります。
array.removeAtIndex(index!)
これは、コンパイルは通りますが、index
はnil
なので、ランタイムエラーが発生します。
つまり
index
の値がnil
じゃ無いときに、配列から要素を削除したいのです。
これを、Optional Bindingを使用しないとこんな感じになると思います。
var array: [String] = ["a", "b", "c"] let index: NSNumber? = find(array, "d") if index != nil { array.removeAtIndex(index!) }
いちいちindex
を定義してnil
判定するのはとても面倒です。
そこでOptional Bindingを使用します。
var array: [String] = ["a", "b", "c"] if let index: NSNumber = find(array, "d") { array.removeAtIndex(index) }
定数の定義とnil
判定を同時に行うことができます。
ifのブロックの中は値がnil
じゃない時に実行されるため、定数はoptionalではありません。
そのため、!
も必要無くなります。
以上、Optional Bindingまとめでした。
【swift】nilとoptionalな型の取り扱い
nilの扱い
swiftでは通常、変数や定数にnil
は代入できません。
var hoge: String = nil let fuga: String = nil
これはコンパイルエラーになります。
nil
を代入するためには、optionalであることを宣言するために型に?
をつけます。
var hoge: String? = nil let fuga: String? // 代入しなくても初期値はnil
メソッドなどを使用する際
変数や定数をoptionalな型として宣言するということは、
「必ずしも値が入っているとは限らない(nilである可能性がある)」
ということです。
その状態でメソッドを叩こうとすれば、当然nil
だった場合は実行できません。
let str: String? str.uppercaseString // strはnilの可能性があるのでコンパイルエラーになる
コンパイルエラーにならないようにするためには、変数や定数の後ろに!
をつけるとコンパイルできるようになります。
let str: String? str!.uppercaseString // コンパイルは通るようになるが実行時にエラーになる
ただ、このケースだとコンパイルは通りますが、str
はあくまでもnil
なので実行時にエラーになります。
この場合str
がnil
でないことを事前にチェックする必要があります。
let str: String? if str != nil { str!.uppercaseString // strがnilではないのでメソッドを実行することができる }
この辺りの記述をもっとスマートに書くために、Optional BindingやOptional Chainingなどの記法がswiftでは提供されているのですが、それについてはまた別途まとめます。
【swift】InterfaceBuilderを使用してViewControllerを組み立てる
objective-cと変わらないと思ってたらちょこっとだけ違いました。
新規ファイル作成から「Cocoa Touch Class」を選択して
「Also create XIB file」にチェックを入れれば、ViewControllerのファイルと一緒にxibファイルを作成されます。
objective-cであれば、initWithNibName
で初期化しなくてもInterfaceBuilderで作成した画面が適用されていたのですが、swiftの場合はnibName
は必ず指定しないとダメっぽい?
self.window?.rootViewController = TestViewController(nibName: "TestViewController", bundle: nil)
【swift】変数・定数・関数を定義する
最近swiftをちょっと勉強しだしたので、ちょっとずつメモを取っていこうと思います。
変数
var hoge: String = "hoge" // 型を宣言する var hoge = "hoge" // 宣言しなくても自動的に判定もする var hoge = "hoge", fuga = "fuga" // カンマで連続で定義することもできる var hoge, fuga: Int // 複数の変数を型宣言することもできる
型宣言は省略することのメリットよりもデメリットの方が大きいので、必ず宣言するようにする。
定数
let HOGE = "hoge"
関数定義
// 返り値がある場合 func say() -> String { return "hello." } // 返り値が無い場合 func say() { println("hello") } // 複数返り値がある場合 func say() -> (Int, Int, Int) { return (1, 1, 1) }
GoogleAnalyticsのトラッキングのコールバックを設定する
〇〇ページに遷移するためのボタンのクリック率とかを図るためによく、ボタンのクリックイベントを計測したりします。
でも、このページ遷移とトラッキングのAPIを同時に叩くと、やっぱりうまくトラッキング出来ないこともチラホラあるみたいです。
Analyticsのページを見ても、明らかに少ないよなーこの数字っていう時が結構あります。
なので、トラッキングのAPIが終わってからページ遷移したいんです。
こういう要望のために、Analyticsはちゃんとコールバックを設定できる様になっています。
hitCallbackを使う
ga.js
もanalytics.js
も基本的には使い方は一緒で、hitCallback
というkeyで関数を指定します。
// ga.js _gaq.push(['_trackPageview', { page: '/index.html', hitCallback: function() { alert('complete!!'); } }]); // analytics.js ga('send', 'pageview', { page: '/index.html', hitCallback: function() { alert('complete!!'); } });
pageviewのAPIが完了してからhitCallback
で設定した関数が実行されます。
注意点
GoogleAnalyticsのjsが読み込めないと正常に動作しないため、コールバックでページ遷移をする処理を書いていると、ページ遷移しないボタンが出来上がってうので注意が必要。
【Ruby】ActiveRecordのAssociationにメソッドを追加する
ActiveRecordのhas_many
とかbelongs_to
とかのAssociationはブロックを渡してメソッドを追加することができます。
class User has_many :blogs do # ステータスがopenのものを取得する def open where(status: 'open') end end end
こんな感じで、ブロックの内部で関数を定義するとその関数をメソッドチェーンで実行できるようになります。
User.first.blogs.open
複数のモデルで使い回したい処理がある場合は、moduleにまとめてextendオプションでメソッドを追加することも可能です。
module Common def open where(status: 'open') end end
class User has_many :blogs, extend: Common end
配列にして、複数のmoduleも渡せます。
class User has_many :blogs, extend: [Common, Common2] end
便利ー!!
【Rails】belongs_toで紐付けたモデルの存在チェック
belongs_to
で紐付けたモデルが本当にデータとして存在するかどうかをチェックする。
class Blog has_many :comments end
class Comment belongs_to :blog end
例えば、このようなモデルがあった時、Blogモデル経由でCommentを作成した場合は、すでにBlogは存在しているので、comment.blog_id
には確実に存在しているBlogのIDが入ります。
Blog.first.comments.create # 存在しているブログからコメントを作成する
ですが、Blogモデル経由ではなくCommentを直接作成するとき、Blogと紐付けるためにはBlogのIDを外側から指定しないといけません。
Comment.create(blog_id: 1) # コメントの作成時にblog_idを指定する
この方法で作成する時、特に設定しないままだと存在しないBlogのIDも指定できてしまいます。
Comment.create(blog_id: 999) # IDが999のBlogは存在しないが、Commentは作成できてしまう
参照先の指定したIDのデータが存在しない場合にエラーを投げるにはValidateを設定する必要があります。
class Comment belongs_to :blog validates_presence_of :blog end
validates_presence_of
で対象を指定すれば、参照先のデータが存在しない場合はエラーになり、データは作成されなくなります。