ThumbFan Keyboard 1.0.1がリリースされました。
今回のアプリでの変更点を見ながら、iOSキーボードやiOS8開発の技術についてまとめておきたいと思います。
カテゴリー: Xcode
片手親指でキータイプができる英語キーボードThumbFan Keyboardをリリース。技術概要など
iOS8でエクステンションの機能が追加されて何か作ってみたかったのと、iPhone 6 Plusの大きさによる不便さを解決したいという理由で、片手親指キーボードをつくってみました。
Objective-Cで、サブクラスだけで使用出来るプロパティを作成する
Objective-Cでクラスを作る場合は、他のクラスからアクセスさせたくないプロパティはクラスエクステンションにして、自クラスだけでアクセス出来るようにします。こんな感じです。
モジュールはこんな感じです。
こうすることによって、notesというプロパティは、参照するクラスからは変更されたくないので、ヘッダーにはreadonly,クラスエクステンションでは、readwriteで定義することによって、クラス内で変更可能です。
booksというプロパティは、クラス内ではNSMutableArrayとして振る舞いたいけれども、対外的には変更してほしくないので、NSArrayとして返しています。booksと別のbooksInternalという内部用プロパティをNSMutableArrayで定義して、内部ではbooksInternalにアクセスすることによって、オブジェクトの追加が出来るようにしています。
このクラスを継承したクラスを作った場合は、対外的には同じくbooks, notesを触られたくないですが、継承したクラス内では触りたい訳です。
モジュールでbooks, notesを触ろうとすると当然ですが、エラーになります。
これを解決するために、クラスエクステンションを別ファイルに出してあげると解決出来ました。
このようにします。
クラスエクステンションの新しいファイルに、プロパティを記述
ベースクラスでは、クラスエクステンションをモジュールでImportします。
継承したクラスでもクラスエクステンションをモジュールでImportします。booksに関しては、内部的にmutableのプロパティ booksInteranlをクラスエクステンションで定義しているのでそちらを使用します。
これで、他のクラスからはprivateExtentionのヘッダーをインポートしないようにすれば定義が見えないので、コンパイル時点で正しい処理が行われていることを確認出来ます。
[Objective-C] libextobjc の @weakify と @strongify について
ちょっと遅れた話題ですが、libextobjcライブラリを使用して、weak変数を使う方法があることを知ったので調査してみました。
これまでの記述法と問題点
これまでわたしは、この記事ARC+Blocks+llvm4.0時代のコード記述作法 | Zero4Racer PRO Developer’s Blog で書いたルールにしたがって、ivar(クラス内の変数)を基本的に使わず、block内で使用する場合に、weak化して使用する方法を使ってきました。WEAKSELFMAKE;というマクロを作成して、selfのマクロを作成しています。
#define WEAKSELFMAKE __weak typeof(self) wself = self
使用する時は、
{
WEAKSELFMAKE;
SomeViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SomeViewController class])];
controller.block_completed = ^ (NSNumber * toReturn) {
wself.numberValue = toReturn.copy;
[wself redlowItems];
}
}
のような感じです。これで結構シンプルに書けてほぼ問題がないのですが、スコープ内のstrongの変数を使う場合は、weak化を自分で行う必要がありました。ちなみにStoryBoardIDをクラス名にしてNSStringFromClass([SomeViewController class])で書くのが最近のお気に入りです。
{
WEAKSELFMAKE;
SomeViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SomeViewController class])];
__weak typeof(controller) wcontroller = controller;
controller.block_completed = ^ (NSNumber * toReturn) {
wself.numberValue = toReturn.copy;
[wself redlowItems];
[wcontroller dismissViewControllerAnimated:YES completion:NULL];
}
}
@weakify, @strongifyで出来ること
jspahrsummers/libextobjc は、Objective-Cを便利にする拡張機能ライブラリです。cocoapodで、
pod 'libextobjc', '~> 0.4'
を追加すれば簡単に追加出来ます。上のサンプルを書き直すと、
{
SomeViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SomeViewController class])];
@weakify(self);
@weakify(controller);
controller.block_completed = ^ (NSNumber * toReturn) {
@strongify(self);
@strongify(controller);
self.numberValue = toReturn.copy;
[self redlowItems];
[controller dismissViewControllerAnimated:YES completion:NULL];
}
}
こんな感じになります。self, controller というオリジナルと同じ変数名を使える代わりに、使う前に、@strongify(self)をしてあげないといけないのがポイントです。
動作原理
Objective-Cのコードに慣れていたらこれが若干気持ち悪いコードに見えます。selfだと循環参照になってしまう気がしてしまうからです。reactive cocoa – Explanation of how weakify and strongify work in ReactiveCocoa – Stack Overflow この記事には、マクロを展開した後のコードが載せられていて分かりやすいです。
これを見ると、@weakifyマクロで self_weak_ というweak変数を作って、@strongifyマクロでself=self_weak_;と新しいself変数を作成していることが分かります。同じ変数名(self)だったら、ローカルスコープのselfの方が優先されるため、selfを使っても循環参照しないんですね。
あと、@weakifyという、予約語を作っているように見えますが、これも強引で、後ろに autorelease (このスクリーンキャプチャの場合は try {} @finally{} 付けることによって、展開後に@autorelease という形になるようにして、無理矢理@予約語のように見せてるんですね。マクロでこんな強引なことが出来るんですね。これの残念なのは、Xcode上での予約された@propertyのようなものと実際には違うので、コード上の色がおかしくなることです。
まとめ
実際これをプロジェクト規約に取り込むかですが、若干微妙なところですね。@strongifyを忘れるとselfを使っているところで循環参照してしまうのと、2行使ってしまうのでコードが煩雑になるので、現在のWEAKSELFMAKE;マクロの方が綺麗な気もします。それで、展開したマクロのself_weak_を使ってあげれば綺麗になると思いました。
{
SomeViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SomeViewController class])];
@weakify(self);
@weakify(controller);
controller.block_completed = ^ (NSNumber * toReturn) {
self_weak_.numberValue = toReturn.copy;
[self_weak_ redlowItems];
[controller_weak_ dismissViewControllerAnimated:YES completion:NULL];
}
}
これで、@strongifyを書き忘れることもないですし、selfを内部で使っていた時に色で見分けるのも簡単になります。しばらくこれで使ってみようと思います。
参考
cocoapods – libextobjc
libextobjc/extobjc at master · jspahrsummers/libextobjc
Safx: libextobjcの@strongifyと@weakifyについて
Objective-C – weakify/strongify マクロを使うと weak self パターンが簡単に書ける – Qiita
CocoaPodsのプロジェクトで、ヘッダーファイルが読め込めない時の対処方法
1つのプロジェクトに複数のターゲットを作成している時に起きる問題のようで引っかかったのでメモ。
cocoaPodsを導入して、podsで導入したフレームワークのヘッダーファイルが読めずに、
19:9: fatal error: ‘RestKit/RestKit.h’ file not found
のようなエラーが出ました。
“CocoaPodsのプロジェクトで、ヘッダーファイルが読め込めない時の対処方法” の続きを読む
「上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編」を書きました[内容紹介あり]
iOS 7向けのiOSアプリ開発を解説した書籍で、わたしも共著で参加せせて頂いた本が12月末に発売予定になり、Amazonにも登録されました。
わたしは今回は以下の部分に参加させていただきました。
- Text Kit(一部)
- Sprite Kit
- 付録のGame Controller
発売に向けて現在は校正などを行っていますが、とても良い本に出来上がっていると思います。わたし以外が書いた部分を読むと特に、分かりやすく書かれているなと感じます。扱われている内容をリストアップすると、 “「上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編」を書きました[内容紹介あり]” の続きを読む
Objective-C の instancetype キーワードが面白い + typeof(self)の考察
Appleのサンプルコード、iAdSuite を見ていたら見慣れない表現が出てきたので、調べて見ました。
@implementation TextViewController
- (instancetype)init
{
self = [super initWithNibName:@"TextViewController" bundle:nil];
if (self) {
//...
}
return self;
}
こんな感じ、”- (instancetype)init”と書いているのを見て、なんで”-(id) init”じゃないのかなと思いました。
“Objective-C の instancetype キーワードが面白い + typeof(self)の考察” の続きを読む
Xcode4.5 子ネターメソッドの戻り値をデバッグ中に見れるようになった!
Xcode4.5でのデバッグの際に、メソッドの戻り値が表示されるようになったのでメモ。
“Xcode4.5 子ネターメソッドの戻り値をデバッグ中に見れるようになった!” の続きを読む
iOS5では使えない…Xcode4.5の新機能、StoryboardでのContainer Viewが便利!
追記:テストの際の勘違いで、iOS5ではクラッシュしてしまいます。iOS5でテストしたつもりのデバイスが、既にiOS6にアップしていました。間違った情報を流してしまい、申し訳ありません。指摘して下さった、内田宏基@東京都 (_danwaneji)さんにも感謝します。
うーん、Storyboardのこれ、iOS5.1のデバイスで実行するとクラッシュしなかったっけ。 >iOS5でも使えるXcode4.5の新機能、StoryboardでのContainer Viewが便利!zero4racer.com/blog/942 @tomohisa さんから
— 内田宏基@東京都さん (@_danwaneji) 9月 25, 2012
Xcode4.5使っているでしょうか?iOS6/iPhone5対応のために使わざるを得ないという方も少なくないと思います。多くの新機能は、iOS6以降のみの機能のため、iOS5対応アプリででは、一部しか使えなかったり、条件判断して使わなければいけなかったりします。今回は、Xcode4.5の新機能で、iOS5プロジェクトでも使える、StoryboardでのContainer Viewについて紹介します。
“iOS5では使えない…Xcode4.5の新機能、StoryboardでのContainer Viewが便利!” の続きを読む
Xcode4.5+iPhone 5小ネタ、XcodeのOrganizerで、新しいデバイスが認識されない場合の対処法
もしかしたら、MacBook Pro with Retina Display特有の問題かもしれません。iPhone 5をMacにつないでも認識されなかったのですが、iTunesのバージョンも最新だし、どうしたことかと思ったのですが、購入時に発生した問題を思い出して、USBを、右側に接続することで無事接続できました。短いエントリですが、備忘のために記録しておきます。