Swiftのprotocolおぼえがき
「Swift実践入門」刊行記念 Tech Talks #swiftjn を聞きに行って、知らないこといっぱいありそうだったので、この本で学んだことをおぼえがきとして残しておきます。
本の裏にも書いてあることを引用しますが、「Swiftは簡潔な言語ですが、その言語仕様を理解し、正しく使うことはけっして容易ではありません。」という言葉、まさしくその通りって感じてます。
『Swift実践入門』の著者の石川さんも言ってましたが、
良い設計は誤った用法をコンパイルエラーにする #swiftjn
— かみぴーさんと呼ばれて2年半 (@kamip_net) 2017年2月24日
大事なことです。
せっかくSwiftで書くならSwiftらしいコードで書けるようになりたい。そのためのインプットとして「Swift実践入門」を教科書代わりに使ってみようと思います。
Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)
- 作者: 石川洋資,西山勇世
- 出版社/メーカー: 技術評論社
- 発売日: 2017/02/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
めんどくさがりやなので、14章から読み始めればOK問題ない、と思ってたけど、いざ読んでみるとしっかり理解していない箇所が結構ありそうなので、とりあえず第7章のprotocolから読みつつ、playgroundで書いたりして遊んでみます。
protocolの基本
ぼんやり知ってたことの復習。ストアドプロパティとかコンピューテッドプロパティという呼び方知らなかった。
protocol SomeProtocol{ var title:String { get } func someMethod(string: String) -> Void nonmutating func nonmutatingSomeMethod(string: String) -> Void mutating func mutatingSomeMethod(string: String) -> Void } struct SomeStruct1 : SomeProtocol { //ストアドプロパティ internal var title: String func someMethod(string: String) { // self.title = string //=> エラー } nonmutating func nonmutatingSomeMethod(string: String) -> Void{ // self.title = string //=> エラー } mutating func mutatingSomeMethod(string: String) -> Void{ self.title = string } } struct SomeStruct2 : SomeProtocol { //コンピューテッドプロパティ internal var title: String { return "default" } func someMethod(string: String) { // self.title = string //=>エラー } mutating func mutatingSomeMethod(string: String) -> Void{ // self.title = string //=>エラー print("nasi") } nonmutating func nonmutatingSomeMethod(string: String) -> Void{ // self.title = string //=>エラー } } class SomeClass : SomeProtocol { var title:String = "" func someMethod(string: String) -> Void{ self.title = string } func nonmutatingSomeMethod(string: String) -> Void{ self.title = string } func mutatingSomeMethod(string: String) -> Void{ self.title = string } } let struct1 = SomeStruct1.init(title: "aaa") struct1.title //struct1.title = "hoge" //#=>エラー //struct1.mutatingSomeMethod(string: "hoge") //#=> struct1をletで生成しているのでエラー struct1.title let struct2 = SomeStruct2.init() struct2.title //struct2.title = "hoge" //#=> コンピューテッドプロパティなのでエラー let someClass = SomeClass.init() someClass.title = "hoge"
associatedtype を使った抽象化
適当なサンプル出したくて消費税とかで書いてみたけど、実装が同じで意味なかった😢
protocol TaxProtocol { associatedtype AssociatedType var tax: AssociatedType { get } func calculate(price: Int) -> AssociatedType } // 実装からAssociatedTypeを自動で定義する方法 struct JapaneseTax: TaxProtocol { var tax: Float func calculate(price: Int) -> Float{ return floor(Float(price) + Float(price) * self.tax) } } let japaneseTax = JapaneseTax(tax: 0.08) japaneseTax.calculate(price: 20100) // typealiasを使ってAssociatedTypeを定義する方法 struct KoreanTax: TaxProtocol { typealias AssociatedType = Float var tax: AssociatedType func calculate(price: Int) -> AssociatedType{ return floor(Float(price) + Float(price) * self.tax) } } let koreanTax = KoreanTax(tax: 10.0) let taxPrice = koreanTax.calculate(price: 20100) // ネスト型AssociatedTypeを定義する方法(使いみちどんな時...?) struct ItarianTax: TaxProtocol { struct AssociatedType {} var tax: AssociatedType func calculate(price: Int) -> AssociatedType{ return AssociatedType() } }
protocol extension によるデフォルト実装 / extension where Self : を使った制約
Tackleって🎣の装備品?をまとめてタックルと言います。
KamipはFishingTackleとBloggerというprotocolを持っているけど、HogeFisherはFishingTackleだけ持っている例。これも適当な思いつきで読みながら進めたので、FishingTackleがwriteDiaryを持っている謎仕様ですが気にしない。HogeとかFooとかで書いたほうがよかったな..。
protocol FishingTackle { var rod: String { get } var reel: String { get } var line: String { get } var lure: String { get } } protocol Blogger { var blogUrl: String { get } } extension FishingTackle { var list: String { return [ "rod: \(self.rod)", "reel: \(self.reel)", "line: \(self.line)", "lure: \(self.lure)", ].joined(separator: "\n") } func printList() -> Void { print(self.list) } } extension FishingTackle where Self : Blogger { func writeDiary() -> Void{ // 日記を書く処理 } } struct Kamip : FishingTackle, Blogger { var rod: String var reel: String var line: String var lure: String var blogUrl: String } let kamip = Kamip( rod: "Daiwa presso-ltd ags ml", reel: "Daiwa 15luvias 2004", line: "SanyoNylon GT-R PINK-SELECTION 2lb", lure: "Jackall tearo 1.6g", blogUrl: "http://kamip.net/" ) print(kamip.list) kamip.printList() print(kamip.blogUrl) kamip.writeDiary() struct HogeFisher: FishingTackle { var rod: String var reel: String var line: String var lure: String } let hogeFisher = HogeFisher(rod: "", reel: "", line: "", lure: "") print(hogeFisher.list) hogeFisher.printList() //hogeFisher.writeDiary() //#=> Bloggerじゃないのでエラーになる
自分で書くと色々試せるのでとてもよい。
次は
標準ライブラリのプロトコルを使う設計が自然にできたら素敵そうだけど、そもそも、標準ライブラリのプロトコル何があるかInputしないと。
class
と struct
の違いも理解が足りなそうなので、次は第5章を読もう。ジェネリクスとかは怖いなー怖いなー。
参考
Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)
- 作者: 石川洋資,西山勇世
- 出版社/メーカー: 技術評論社
- 発売日: 2017/02/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
remoteConfig.fetchWithExpirationDuration の callback の呼び出しタイミングがとても遅い件
FirebaseのremoteConfigを試してたのですが、以下の ...のcallbackが呼ばれるのがとっても遅い。
remoteConfig.fetchWithExpirationDuration(NSTimeInterval(1)) {...}
controllerの処理が終わったらcallbackが動き出す感じ。なんでだろう。
理想は func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?)
ここでリモートのデータを取得したかったのだけど出来そうもない。普通に自家製APIつくって同期処理のリクエスト作って dispatch_semaphore_wait
使ってやればやりたいことは実現できそうなのだけど、 remoteConfig
使えなくてつらい。
dispatch_semaphore_createを使った別スレッドの処理を待つ書き方
同期処理というのかな?並列処理とか苦手だなぁ。
print("hoge-1") let semaphore = dispatch_semaphore_create(0) print("hoge-2") let queue = dispatch_get_main_queue() print("hoge-3") dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { print("hoge-4") sleep(10) print("hoge-5") dispatch_semaphore_signal(semaphore) print("hoge-6") }) print("moge-1") dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) print("moge-2")
結果は
hoge-1 hoge-2 hoge-3 hoge-4 moge-1 hoge-5 hoge-6 moge-2
bundle install 時に使われるrubyのバージョンがおかしい
gem list
ここに bundler がない場合におかしくなる様子
なので
gem install bundler
を行い、
rbenv rehash
してから再度
bundle install --path vendor/bundle
するといい感じになる
Sierraで rails c すると「uninitialized constant Mysql2::Client::SECURE_CONNECTION (NameError)」
新しいPC(Sierra)で既存Railsアプリケーションを構築中に rails c
したら以下のようなエラーがでる
uninitialized constant Mysql2::Client::SECURE_CONNECTION (NameError)
mysqlがインストールされていないと思って brew install mysql
して直るだろうと思ってインストールしてみて再度 rails c
すると以下のようなエラーがでる
Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.20.dylib (LoadError)
つらい パニック
しかしggって調べてたらピンときて mysql5.6
が必要なのに mysql5.7
がインストールされてるのが原因っぽい
ので一旦、インストール済みの mysql5.7 をアンインストール brew uninstall mysql
してから、mysql5.6 をインストールする brew install homebrew/versions/mysql56
そしてすでに bundle install しているファイルを消す必要がある
私の場合は rm -rf ./vendor/bundle
で消える
削除が完了したら再度 bundle install --path vendor/bundle
おういえ。
bundle install したら eventmachine でエラーが起こる
bundle install --path vendor/bundle したときに以下のエラーで困った時
An error occurred while installing eventmachine (1.0.3), and Bundler cannot continue.
Make sure that `gem install eventmachine -v '1.0.3'` succeeds before bundling.
以下を実行して予めビルドオプションを設定しておくとよい。
bundle config build.eventmachine --with-cppflags=-I/usr/local/opt/openssl/include
内容は cat ~/.bundle/config ここに記載される。
xcode7で謎エラー
xibを表示するとなぞのエラーが出ていたのでいろいろ調べた結果、
Failed to update auto layout status. ... the agent crashed
xcodeを再起動したら直った件
『 failed to update auto layout status the agent crashed』とかでググってる人は試してみるといいかも