Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2011-08-28

Push Notification おさらい

プッシュ通知を実装するときに、「バックグラウンドで通知受けたときはapplication:didFinishLaunchingWithOptions:とapplication:didReceiveRemoteNotification:、どっちが呼ばれるんだっけ?」とかなんだかいつも自信なくなってきてドキュメントを見返したりしてしまうので、ドキュメントと、実際の動作を確認した内容をまとめておきます。


(参考ドキュメント)

Local Notification および Push Notification プログラミングガイド(日本語)

英語・最新版


アプリがバックグラウンドで動作中に通知が配信された場合の挙動

この場合、システムは、警告の表示、アイコンのバッジの表示、また、サウンドの再生を行う ことで通知を提示します。

アクションボタンがタップされると、システムはアプリケーションを起動します。そのアプリケーションはデリゲートのapplication:didFinishLaunchingWithOptions:メソッド(実装されている場合)を呼び出し、通知のペイロード(Remote Notificationの場合)またはLocal Notificationオブジェクト(Local Notificationの場合)を渡します。


アプリがフォアグラウンドで動作中に通知が配信された場合の挙動

アプリケーションは、そのデリゲートのapplication:didReceiveRemoteNotification:メソッド(Remote Notificationの場合)またはapplication:didReceiveLocalNotification:メソッド(Local Notificationの場合)を呼び出して、通知のペイロードまたはLocal Notificationオブジェクトを渡します。


ユーザがアクションボタンをタップしたからアプリケーションが起動されたのか、アプリケーション状態を確認したことによって動作中のアプリケーションに通知が配信されたのかどうかを特定できます。デリゲートのapplication:didReceiveRemoteNotification:メソッドまたは application:didReceiveLocalNotification:メソッドの実装で、applicationStateプロパティの値を取得し、その値を評価します。値がUIApplicationStateInactiveの場合は、ユーザがアクションボタンをタップしました。値がUIApplicationStateActiveの場合は、通知を受け取ったと きにそのアプリケーションが最前面にありました。

ここの「アプリケーション状態を確認したことによって動作中のアプリケーションに通知が配信されたのかどうか」ってのが何のことかよくわからなかったので原文みると、

You can determine whether an application is launched as a result of the user tapping the action button or whether the notification was delivered to the already-running application by examining the application state.

とあって、つまり「フォアグラウンドにあるアプリケーションに通知が配信されたのかどうか、ApplicationStateを確認することで特定できる」ということを言ってるようです。


まとめ

フォアグラウンドでプッシュ通知を受け取ったとき

  • application:didReceiveRemoteNotification:メソッドが呼ばれる
  • application:didReceiveRemoteNotification:でのUIApplicationStateActiveの値はUIApplicationStateActive

アプリのプロセスがバックグラウンドで生きているときにプッシュ通知を受け取り、ユーザーがアクションボタンをタップしたとき

  • application:didReceiveRemoteNotification:メソッドが呼ばれる
  • application:didReceiveRemoteNotification:でのUIApplicationStateActiveの値はUIApplicationStateInactive

アプリのプロセスがバックグラウンドで生きていないときにプッシュ通知を受け取り、ユーザーがアクションボタンをタップしたとき

  • application:didFinishLaunchingWithOptions:メソッドが呼ばれる
  • application:didFinishLaunchingWithOptions:の引数launchOptionsにUIApplicationLaunchOptionsRemoteNotificationKeyというキーでペイロードが入ってくる


2011-08-27

パフォーマンスチューニングに関するアップルのドキュメント

アップルの『 iOSアプリケーションプログラミングガイド英語版)』に、『パフォーマンスと応答性のチューニング』という章があって、これが今読むとかなり参考になったので、引用します。


個人的には、「リソースをあらかじめロードしておくと高速化になりそうだけど、メモリの無駄遣いになって結果的に遅くなるので絶対にやるな」というのが一番ささりました。

画像ファイルはUIImage化しておくと使うときに速そうだなーとか考えてたところだったので。


他にも個人的にためになった部分を太字にしてあります。



メインスレッドを妨害しない

アプリケーションのメインスレッド上で実行する処理のタイプを必ず制限します。メインスレッドは、アプリケーションがタッチイベントやその他のユーザ入力を処理する場所です。 アプリケーションが常にユーザに応答することを保証するには、時間のかかるタスクの実行や際限なく続く可能性のあるタスク(ネットワークにアクセスするタスクなど)の実行にメインスレッドを使用すべきではありません。代わりに、これらのタスクは常にバックグラウンドのスレッドに移すべきです。そのためによく使われるのは、Grand Central Dispatch (GCD)や操作オブジェクトを使用して非同期にタスクを実行する方法です。

タスクをバックグラウンドに移すことでメインスレッドは解放されてユーザ入力の処理を継続できます。これは、アプリケーションの起動時や終了時には特に重要です。起動時や終了時は、アプリケーションはタイムリーにイベントに応答することを期待されます。 起動時にアプリケーションのメインスレッドが機能しないと、起動が完了する前にシステムがアプリケーションを強制終了することもあります。 終了時にメインスレッドが機能しないと、重要なユーザデータを書き込む前にシステムが同様にアプリケーションを強制終了することもあります。

サクサク動くUIの実現において超重要事項。操作オブジェクトというのは原文を読むとoperation objectとあるので、たぶんNSOperationとかのこと。


メモリを効率的に使用する

iOSの仮想メモリモデルにはディスクのスワップ領域が含まれていないため、アプリケーションが使用できるメモリ量はさらに制限されます。大量のメモリを使用すると、深刻なシステムパフォーマンスの低下が生じ、システムがアプリケーションを強制終了する結果になります。 さらに、マルチタスク環境で実行中のアプリケーションは、ほかのすべてのアプリケーションとシステムメモリを共有しなければなりません。 このため、アプリケーションが使用するメモリ量を削減することに高い優先度を置きます。

利用可能な空きメモリの量と、アプリケーションの相対的なパフォーマンスの間には、直接の相関があります。空きメモリがほとんどないと、システムが将来のメモリ要求を満たすことができない 可能性が高くなります。この状態が発生した場合、システムはいつでも、一時停止中のアプリケーション、コードページ、その他の不揮発性リソースをメモリから削除できます。 ただし、これらの アプリケーションやリソースを削除しても、一時的な解決にしかなりません。特に、それらがすぐに必要になる場合はそうです。代わりに、まず第一にメモリの使用を最小限にして、本当に使用するメモリを適切なタイミングでクリーンアップします。


メモリ不足警告を監視する

システムがメモリ不足の警告をアプリケーションに送ってきた場合は、即座に対応する必要があります。空きメモリの容量が安全しきい値を下回ると、iOSは実行中のアプリケーションに通知します (ただし、一時停止中のアプリケーションには通知しません)。 アプリケーションがこの警告を受け取った場合は、できるだけ多くのメモリを解放しなければなりません。解放するのに最適なメモリは、キャッシュと画像オブジェクトです。これらは、必要であれば後から再作成できます。

UIKitでは、メモリ不足の警告を受け取るための方法をいくつか提供しています。以下の方法が含まれます。

  • アプリケーションデリゲートのapplicationDidReceiveMemoryWarning:メソッドを実装する。
  • UIViewControllerのカスタムサブクラスで、didReceiveMemoryWarningメソッドをオーバーライドする。
  • UIApplicationDidReceiveMemoryWarningNotification通知を受け取るように登録する。

このような警告を受け取ったら、ハンドラメソッドではただちに不要なメモリを解放して対応する必要があります。たとえば、UIViewControllerクラスのデフォルトの動作では、ビューが現在表示されていなければ、そのビューをメモリから削除します。サブクラスでは、追加したデータ構造体をメモリから削除することによってこのデフォルトの動作を補完できます。画像のキャッシュを管理しているアプリケーションでは、現在画面上にない画像をすべて解放することによって対応できます。

データモデルに既知の消去可能なリソースが含まれている場合は、対応するマネージャオブジェクトがUIApplicationDidReceiveMemoryWarningNotification通知を受け取れるように登録して、消去可能なリソースを直接解放させることもできます。 この通知を直接処理することによって、アプリケーションデリゲートを介してすべてのメモリ警告呼び出しをルーティングする必要がなくな ります。


アプリケーションのメモリ占有量を削減するためのヒント
  • メモリリークをなくす
  • リソースファイルをできる限り小さくする

ファイルはディスク上に存在しますが、使用する前にはメモリにロードしなければなりません。プロパティリストファイルと画像は、非常に簡単な操作でサイズを小さくできます。 プロパティリストファイルが使用するメモリを削減するには、NSPropertyListSerializationクラスを使用して、これらのファイルをバイナリ形式で保存します。画像については、すべての画像ファイルをできる限り小さくなるように圧縮します (PNG画像を圧縮するに は、pngcrushツールを使用します)。

  • 大きなデータセットに はCore Dataまたは SQLiteを使用する

アプリケーションで大量の構造化データを扱う場合は、そのデータをフラットなファイルではなく、Core Dataの永続ストアまたはSQLiteデータベースに保存します。Core DataおよびSQLiteは、データセット全体をメモリに一度にロードすることを要求することなく大容量のデータセットを管理する効率的な方法を提供しています。

  • リソースは後からロードする

実際に必要になるまでは、リソースファイルをロードしてはいけません。 リソースファイルを事前にロードすることは時間を節約する方法のよう に思えますが、実際には、すぐにアプリケーションの速度が遅くなります。 また、そのリソースを使用せずに終わった場合は、リソースのロードは単なるメモリの無駄使いです。

  • Thumbオプションを使用してプログラムをビルドする。

コンパイラフラグに-mthumbを追加すると、コードサイズを最大35%まで削減できます。 ただし、アプリケーションに浮動小数点を多用するコー ドモジュールが含まれており、ARMv6用にアプリケーションをビルドしている場合は、Thumbオプションを無効にするべきです。 ARMv7用にアプリケーションをビルドしている場合は、Thumbオプションを有効のままにしておくべきです。


メモリを賢く割り当てる

プログラム内でメモリを割り当てる際のヒント

  • 自動解放オブジェクトの使用を減らす

autoreleaseメソッドを使用して解放されたオブジェクトは、現在の自動解放プールを明示的に空にするか次のイベントループに入るときまで、メモリに残ります。できる限り、autoreleaseメソッドの使用は避けて、代わりに、オブジェクトが占有していたメモリがすぐに解放されるreleaseメソッドを使用します。ある程度の数の自動解放オブジェクトを作成しなければな らない場合は、ローカルの自動解放プールを作成し、定期的にそれを空にすることで、次のイベントループに入る前にこれらのオブジェクトに割り当てられたメモリを解放します。

  • リソースにサイズ の上限を設ける

小さなリソースファイルで済む場合は、大きなリソースファイルのロードは 避けます。高解像度の画像を使用する代わりに、iOSベースのデバイスに適したサイズの画像を使用します。大きなリソースファイルを使用しなければ ならない場合は、その時に必要なファイルの部分だけをロードする方法を見つけます。たとえば、ファイル全体をメモリにロードする代わりに、mmap関数とmunmap関数を使用して、ファイルの一部をメモリにマップしたりアンマップしたりします。メモリへのファイルのマッピングの詳細については、 『File-System Performance Guidelines』を参照してください。

  • 無限問題セットは 避ける

無限問題セットは、際限なく大量のデータの計算を必要とする可能性があり ます。このセットが、利用可能な量よりも多くのメモリを必要とした場合、 アプリケーションは計算を完了できなくなります。このようなセットの使用はできる限り避け、メモリの上限が明確な問題を処理すべきです。


浮動小数点演算の留意点

iOSベースのデバイスのプロセッサは、浮動小数点演算をハードウェアで実行することができます。ソフトウェアベースの固定小数点算術ライブラリを使用して計算を実行する既存のプログラムがある場合は、その代わりに浮動小数点算術ライブラリを使用するように変更することを検討すべきで す。ハードウェアベースの浮動小数点演算は、一般に、それと同等のソフトウェアベースの固定小数点演算よりもはるかに高速です。

iOS 4以降では、Accelerateフレームワークの関数を使用して複雑な数値計算を行うこともできます。 このフレームワークには、デジタル信号処理と線形代数計算用に、ベクトル化により高速化された 高性能のライブラリが含まれています。これらのライブラリをオーディオ処理やビデオの処理、物 理学、統計学、暗号学、複雑な代数式を伴う問題に適用できます。


電力消費を抑える

iOSの電源管理システムは、現在使われていないハードウェア機能をすべて停止することによって電力を節約します。 以下の機能の使用を最適化すると、バッテリー持続時間を長くするのに役立ちます。

  • CPU
  • Wi-Fi、Bluetooth、およびベースバンド(EDGE、3G)無線
  • Core Locationフレームワーク
  • 加速度センサー
  • ディスク
  • ポーリングが必要な作業の実行は避ける。ポーリングによってCPUはスリープ状態に入れなくなります。 ポーリングの代わりに、NSRunLoopクラスまたはNSTimerクラスを使用して必要に応じて作業をスケジューリングします。
  • 共有のUIApplicationオブジェクトのidleTimerDisabledプロパティは、できる限りNOに設定したままにする。 アイドルタイマーは、アクティブでない状態が一定時間続くとデバイスの画面をオフにします。アプリケーションで画面をオンにしておく必要がない場合は、システムが画面をオフにするのを許可します。 画面がオフになることによってアプリケーションに副次的な影響が生じる場合は、不必要にアイドルタイマーを無効にするのではなく、その副次的な影響がなくなるようにコードを修正すべきです。
  • アイドル時間を最大にするためにできる限り作業をまとめる。 一般に、一連の演算を一度にすべて実行する方が、それらを小さなまとまりに分けて長い期間にわたって実行するよりも電力の消費が少なくなります。 小さな作業を定期的に実行すると、頻繁にCPUを作動させて作業を 実行できる状態にする必要があります。
  • ディスクへの過剰アクセスは避ける。 たとえば、アプリケーションが状態情報をディスクに保存する場合は、状態情報が変化したときだけ保存を行いできる限り変更をまとめて小さな変更を頻繁に書き込むことを避けるようにします。
  • 必要以上に高速に画面に描画しない。描画は電力の消費においては高価な操作です。フレームレートを抑えるためにハードウェアに依存してはいけません。アプリケーションで実際に必要なフレームのみを描画します。
  • UIAccelerometerクラスを使用して定期的な加速度センサーイベントを受信する場合、必要がないときはこれらのイベントの配信を無効にする。同様に、イベントの配信頻度をニーズに合った最小値に設定します。 詳細については、『Event Handling Guide for iOS』を参照してください。
  • ネットワークに伝送するデータが多いほど、無線を実行するために使用する電力は増加します。 実際、ネットワークにアクセスすることは、最も電力を消費する操作です。
    • 外部ネットワークサーバには必要なときだけ接続し、サーバのポーリングは行わない。
    • ネットワークに接続しなければならない場合は、ジョブを実行するために必要な伝送データ量を最小限に抑える。コンパクトなデータ形式を使用し、無視されるだけの余分なコンテンツは含めないようにします。
    • データは、複数の伝送パケットに分散して時間をかけて伝送するのではなく、一括して伝送する。システムは、Wi-Fi無線や携帯電話用無線が使われていないことを検出すると、それをオフにします。同じ量のデータを伝送した場合、伝送時間が長いほど、短い時間で伝送したときよ りも多くの電力を消費します。
    • できる限りWi-Fi無線を使用してネットワークに接続する。 Wi-Fi無線のほうが消費電力が少ない ため、携帯電話無線よりも推奨されます。
  • Core Locationフレームワークを使用して位置データを収集する場合は、位置の更新をできるだけ早く無効にし、距離フィルタと精度レベルを適切な値に設定する。 Core Locationは、利用可能なGPS、携帯電話、およびWi-Fiネットワークを使用して、ユーザの位置を決定します。Core Locationではこれらの無線の使用を最小にするように精一杯努力していますが、Core Locationの精度およびフィルタの値を設定することによって、必要がない場合はハードウェアを完全にオフにすることができます。詳細については、『Location Awareness Programming Guide』を参照してください。

ファイルアクセス時間の改善

ディスクに書き込むデータ量を最小限にする

ファイル操作は、比較的低速であり、寿命が限られているフラッシュディスクへの書き込みを伴います。ファイル関連操作を最小限にするた めの具体的なヒントを、以下に示します。

  • ファイルの変更部分だけを書き込み、変更はできるだけまとめる。わずか数バイトの変更のために、ファイル全体を書き込むようなことは避けるようにします。
  • ファイル形式を定義する際には、毎回ディスクに書き込む必要があるブロック数が最少になるように、頻繁に更新されるコンテンツをグループにまとめる。
  • データが、ランダムアクセスされる構造化された内容で構成される場合は、それをCore Data 永続化ストアまたはSQLiteデータベースに保存します。 特に、操作対象のデータの量が数メガバイトを超える場合は、そうします。
ディスクへのキャッシュファイルの書き込みを避ける

この規則の唯一の例外は、アプリケー ションを終了する際、次の起動時に同じ状態へアプリケーションを戻すために使用する、状態情報を書き込む必要がある時です。


効率的なネットワーク処理のためのヒント

ネットワーク経由で送受信を行うコードを実装する場合、それはデバイス上で最も電力を集中的に使う操作の1つです。 データの送受信にかける時間を最小限にすることがバッテリー持続時間の向上につながります。そのためには、ネットワーク関連コードの作成時には、以下のヒントを考慮してください。

  • デベロッパのコントロール下にあるプロトコルについては、可能な限りコンパクトなデータ形式を定義する。
  • チャットプロトコルの使用を避ける。
  • 可能な場合はデータを一括転送する。

携帯電話無線機能およびWi-Fi無線機能は、アクティブでないときには電源がオフになるように設計されています。しかし、無線の状態によりますが、これには数秒間かかる場合があります。アプリケーションが数秒ごとに少量データを転送すると、実際には何もしていなくても無線機能の電源がオフにならずに、電力を消費し続ける可能性があります。頻繁に少量のデータを転送するよりも、多量のデータを一度に転送するか、比較的長い間隔で転送するほうが良いでしょう。

ネットワーク経由で通信するときは、常にパケット損失が生じる可能性があります。 そのため、ネットワークのコードを作成する際には、エラー処理に関してできる限り堅牢であるようにすべきです。ネットワーク状態の変化に対応するハンドラを実装することは理にかなっていますが、これらのハンドラが一貫性を持って呼び出されなくても驚かないでください。たとえば、Bonjourネットワーキングのコールバックは、ネットワークサービスが失われても、必ずしもすぐに呼び出されるわけではありません。 Bonjourシステムサービスは、サービスが失われたという通知を受け取るとブラウズコールバックをすぐに呼び出しますが、ネットワークサービスは通知なく失われる場合もあります。たとえば、ネットワークサービスを提供しているデバイスが予期しないときにネットワーク接続を失ったり、通知が転送中に失われたりする可能性があります。


Wi-Fiの使用

Wi-Fi無線を使用してネットワークアクセスをする場合は、そのことをシステムに通知する必要があります。それには、アプリケーションのInfo.plistファイルにUIRequiresPersistentWiFiキーを含めます。このキーを含めると、アクティブなWi-Fiホットスポットを検出したら、ネットワーク選択ダイアログにそれを表示する必要があることをシステムに知らせることができます。また、アプリケーションの実行中はWi-Fiハードウェアを停止させてはならないことをシステムに知らせること もできます。

Wi-Fiハードウェアが電力を消費し過ぎるのを防ぐために、iOSにはタイマーが内蔵されており、30分経過しても、実行中のアプリケーションがUIRequiresPersistentWiFiキーを通じてWi-Fiハードウェアの使用を要求しなかった場合はハードウェアの電源を完全に切ります。 ユーザがこのキーを含むアプリケーションを起動すると、iOSは、アプリケーションが実行している間は、事実上このタイマーを無効にします。ただし、アプリケーションが終了または一時停止したらすぐに、システム はタイマーを有効にし直します。


注: UIRequiresPersistentWiFiの値がtrueであっても、デバイスが待機中(つまり画面がロック されている状態)のときにはWi-Fi接続は機能しません。アプリケーションはある程度は動作します が、アクティブではないと見なされWi-Fi接続は行われません。


UIRequiresPersistentWiFiキーおよびInfo.plistファイルのキーの詳細については、「情報プロパティリスト」 (93 ページ)を参照してください。


機内モード警告

デバイスが機内モードにあるときにアプリケーションが起動した場合、システムがその事実をユー ザに通知するために警告を表示することがあります。 システムがこの警告を表示するのは、次の条 件をすべて満たした場合に限られます。

  • アプリケーションの情報プロパティリスト(Info.plist)ファイルにUIRequiresPersistentWiFi キーが含まれており、その値がtrueに設定されている場合。
  • デバイスが機内モードでアプリケーションを起動した場合。
  • デバイスのWi-Fiが、機内モードに切り替わった後、手動で有効に戻されていない場合。


2011-08-26

Objective-Cの.gitignoreテンプレート

同僚のFacebook投稿で知ったのですが、各種言語の.gitignoreテンプレートを集めたリポジトリがあります。

https://github.com/github/gitignore


ちなみにObjective-Cのテンプレートの中身はこんな感じでした。

https://github.com/github/gitignore/blob/master/Objective-C.gitignore

build/*

*.pbxuser

!default.pbxuser

*.mode1v3

!default.mode1v3

*.mode2v3

!default.mode2v3

*.perspectivev3

!default.perspectivev3

*.xcworkspace

!default.xcworkspace

xcuserdata

profile

*.moved-aside

2011/8/26現在の最終更新はNovember 13, 2010となっているので、最近のXcode用としてはちょっと古いかもしれません。



2011-08-25

CATransform3D で 複数軸に沿って回転させる場合の注意点

たとえば、

  • y軸について110度回転
  • z軸について30度回転
CATransform3D transformY = CATransform3DRotate(transform3D, DEGREES_TO_RADIANS(110.0), 0, 1, 0);
CATransform3D transformZ = CATransform3DRotate(transform3D, DEGREES_TO_RADIANS(30.0), 0, 0, 1);

という2つの変換行列CATransform3Dがある場合に、


y軸について110度回転させてから

z軸について30度回転

CATransform3D concatenated = CATransform3DConcat(transformY, transformZ);

と、


z軸について30度回転させてから

y軸について110度回転

CATransform3D concatenated = CATransform3DConcat(transformZ, transformY);

は全然違う結果になります。


なかなか思い通りの結果にならなくて、手元のiPhoneをアレコレ回転させてみてやっと気づいたこの事実。


「行列の掛け算は順番によって結果が違う」っていう高校で習ったアレはこのことだったのか・・・!



2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |