iOSのバージョンチェック方法

今週は、iPhone 4Sの発売が控えていますが、開発者にとっては、もう1つ重要なポイントがあります。それは、iOSが、5.0にバージョンアップされることです。
SysStats MonitorとSysStats Liteでは、バージョンアップの影響を受ける部分があります。
過去に、2.2.1でビルドしたアプリから、3.0で追加されたAPIを使ってみました - The iPhone Development Playgroundで、メソッドが定義されているかどうかをチェックする方法を紹介しましたが、今回は、実行中のOSのバージョンをチェックするより他に方法がありませんでした。
簡単な処理ですが、そのチェック方法について、メモを兼ねて、書いておこうと思います。

    NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion];

    if ([currentSystemVersion compare:@"5.0" options:NSNumericSearch] == NSOrderedAscending) {
        
        // 5.0より前のバージョンと判定されたとき
        NSLog(@"currentSystemVersion(%@) < 5.0", currentSystemVersion);
        
    }
    
    if (([currentSystemVersion compare:@"5.0" options:NSNumericSearch] == NSOrderedSame) ||
        ([currentSystemVersion compare:@"5.0" options:NSNumericSearch] == NSOrderedDescending)) {
        
        // 5.0以降のバージョンと判定されたとき
        NSLog(@"currentSystemVersion(%@) >= 5.0", currentSystemVersion);
        
    }

-[NSString compare:options:]メソッドのoptionsにNSNumericSearchを指定すると、中に含まれている数字を考慮して比較してくれるので、楽ですね。

UIDocumentInteractionControllerによる、アプリケーション間のファイル受渡し

昨年、iOSの中で、アプリケーション同士が連携するためのしくみ - The iPhone Development Playgroundで、UIDocumentInteractionControllerを使ったアプリケーション間連携について簡単に説明しました。そのときに少し触れたとおり、SysStats MonitorSysStats Liteの間で、UIDocumentInteractionControllerを使った連携機能を追加しましたので、もう少し技術的な説明をしたいと思います。

処理の流れ

以下に、全体の流れを示します。

ここでは、以下の2つの目的で、UIDocumentInteractionControllerを使った連携を使用しています。

(1) SysStats Liteから、SysStats Monitorへのプロセスブックマークの移行

SysStats Liteで蓄積したプロセスブックマークを、SysStats Monitorで流用するための機能です。

(1-1) Send ユーザの操作により、UIDocumentInteractionController経由で、SysStats Monitorが自動的に起動され、プロセスブックマークデータを送られる。
そして、SysStats Monitor側のInboxと呼ばれるフォルダに、そのデータが保存される。
(1-2) Import ユーザの操作により、Inboxからブックマークデータを読み込み、現在のブックマークデータにインポートする。
(2) SysStats Monitorのプロセスブックマークを、Email経由で共有

異なるデバイスのSysStats Monitor間でプロセスブックマークを共有するための機能です。
例えば、iPhoneiPadを所有しているユーザの場合、iPhoneで蓄積したプロセスブックマークを、iPad側に取り込む(Import)ことができます。

(2-1) Export ユーザの操作により、Message UI Frameworkを使って、現在のブックマークデータ添付ファイルとするメールを作成する。
(2-2) Send Emailとして送信する。
(2-3) Send 受信側のデバイスで、Mail.appでEmailを受信し、添付ファイルを開く操作をすることで、SysStats Monitorにプロセスブックマークデータを送ることができる。
(1-1)と同様に、SysStats Monitorが自動起動され、Inboxフォルダに、そのデータが保存される。
(2-4) Import ユーザの操作により、Inboxからブックマークデータを読み込み、現在のブックマークデータにインポートする。

プロセスブックマークデータのUTI宣言

Appleは、データタイプの識別子のための構文として、UTI(uniform type identifiers)を定義しています。
UIDocumentInteractionControllerに、データタイプを認識させるためには、そのデータのUTIを宣言する必要があります。

参考ドキュメント

UTIに関しては、Uniform Type Identifiers Overviewを参照してください。
Info.plistの各項目の設定値については、Information Property List Key Referenceを参照してください。

SysStats MonitorのInfo.plistに、以下のような設定を行っています。

	<key>UTExportedTypeDeclarations</key>
	<array>
		<dict>
			<key>UTTypeIdentifier</key>
			<string>[プロセスブックマークのUTI]</string>
			<key>UTTypeDescription</key>
			<string>SysStatsMonitor&apos;s proprietary processbookmark format</string>
			<key>UTTypeConformsTo</key>
			<array>
				<string>public.item</string>
			</array>
			<key>UTTypeTagSpecification</key>
			<dict>
				<key>public.filename-extension</key>
				<array>
					<string>[プロセスブックマークの拡張子]</string>
				</array>
				<key>public.mime-type</key>
				<string>[プロセスブックマークのMIMEタイプ]</string>
			</dict>
		</dict>
	</array>
UTTypeIdentifier プロセスブックマークのUTIを指定しています。
UTTypeTagSpecification プロセスブックマークの拡張子MIMEタイプを指定しています。
UTTypeConformsTo UTIは、適合性(Conformance)に基づく階層構造の中に定義します。
プロセスブックマークは、SysStats Monitor独自のデータフォーマットなので、public.itemという、最上位階層を指定しています。
テキストファイルであるからといって、public.textを指定した場合、次で説明するLSItemContentTypesにpublic.textが指定されているすべてのアプリで開くことが可能となってしまうので、注意が必要です。

SysStats Monitorがプロセスブックマークを受取るための設定

SysStats MonitorのInfo.plistに、以下のような設定を行っています。

	<key>CFBundleDocumentTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeName</key>
			<string>SysStatsMonitor&apos;s proprietary processbookmark format</string>
			<key>CFBundleTypeRole</key>
			<string>Viewer</string>
			<key>LSHandlerRank</key>
			<string>Owner</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>[プロセスブックマークのUTI]</string>
			</array>
		</dict>
	</array>

ここでのポイントは、LSItemContentTypesに、前述のUTTypeIdentifierにて指定した[プロセスブックマークのUTI]を指定しているところです。この設定によって、SysStats Monitorは、プロセスブックマークを受取ることができるようになります。

詳細は、Document Interaction Programming Topics for iOSの"Registering the File Types Your App Supports"を参照してください。

ドキュメントを送る側のコーディングイメージ

SysStats Liteの設定画面で、「SysStats Monitorへエクスポート」を選択すると、以下のような、ドキュメント送信先のアプリを選択するメニューが表示されます。

実際の処理は、UIDocumentInteractionControllerを使って以下のように記述されています。

    self.docController = [UIDocumentInteractionController interactionControllerWithURL:url];
    docController.delegate = self;
    BOOL isValid = [docController presentOpenInMenuFromRect:CGRectZero inView:self.tabBarController.view animated:YES];
    if (!isValid) {
        // 必要に応じてエラー処理(AlertPanel)
    }

メニューから、「SysStatsMon」が選択された後、UIDocumentInteractionControllerDelegateメソッドが呼び出されます。SysStats Liteでは、以下のように特に何もしていませんが、各タイミングで必要な処理があれば、それぞれに記述します。

#pragma mark UIDocumentInteractionControllerDelegate

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application {
    NSLog(@"!!!!! Sending to: %@", application);
}

- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
    NSLog(@"!!!!! Sent to: %@", application);
}

- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
    self.docController = nil;
}

ドキュメントを受取る側のコーディングイメージ

以下に、ドキュメントを受取る側である、SysStats Monitorのコーディングイメージを示します。

UIDocumentInteractionControllerDelegate経由で起動されると、application:didFinishLaunchingWithOptions:メソッドが呼ばれます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    if (launchOptions) {
        NSURL *launchedURL = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
        if ([launchedURL isFileURL]) {
            if ([[launchedURL pathExtension] isEqualToString:kProcessBookmarkFileExtention]) {
                
                // 確認メッセージのためのファイル名などの情報を取得
                NSString *fileName = [launchedURL lastPathComponent];
				...

				// AlertPanelを表示
                
            }
            
        }
        
    }
    return YES;
    
}

SysStats Monitorでは、プロセスブックマークを受け取った時に、以下のような確認メッセージを表示しています。

[補足] Inboxフォルダのパスは、以下のようにして取得できます。
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *inboxPath = [[paths objectAtIndex:0] stringByAppendingString:@"/Inbox"];

iOS4.2で、一部の標準アプリのプロセス名が変更されたようです


SysStats Monitor / Lite では稼働中のプロセス一覧を表示しています。実際のプロセス名と、ホーム画面に表示されている名前と一致している場合もありますが、それが異なっているものも多いのが実情です。
例えば、以下のアプリが該当します。

ホーム画面 プロセス名
Safari MobileSafari
iTunes MobileStore
メモ MobileNotes

このように、ただ単にプロセス名の一覧を表示するだけではわかりにくいので、SysStats Monitor / Liteでは、「プロセスブックマーク」という機能をつけて、ユーザがわかりやすい名前を付与して、その名前で表示できるようにしています。言い換えると、プロセス名の辞書のようなものです。ただ、すべてのプロセスに関してその作業を行うのはかなり面倒な作業となるので、いくつかの標準アプリについては、事前に登録済みになっています。

ところが、このプロセス名というのは、特に仕様として公開されているものではなく、あくまでも、システム内部で使用されている名称に過ぎません。予告なしに変更されることは、十分に予測されます。標準アプリだけでなく、みなさんが、自分でインストールした一般のアプリも同様です。
そういったことを織り込み済みで、このような方式を採用しています。

さて、本題に入りますが、iOS4.2からプロセス名が変更になったものがあることが判明しました。それも、機種ごとに異なる名前になっているようです。
以下に、iPhoneiPadの例を示します。


整理すると、以下の感じで、いずれもチルダ(~)のあとに、機種名が付加されています。*1

ホーム画面 プロセス名(4.1以前) プロセス名(iPhone4.2) プロセス名(iPad4.2)
連絡先 Contacts Contacts~iphone Contacts~ipad
マップ Maps Maps~iphone Maps~ipad
Game Center Game Center Game Center~ipho Game Center~ipad

その他、iPhoneでは、標準のカメラアプリのプロセスが独立したプロセスになったようです。
SysStats Monitor / Liteのユーザの方は、上記のアプリを起動し、ブックマークに登録しなおすことで、表示できるようになります。
次期バージョンでは、このあたりを考慮して、事前登録の内容を見直す予定です。

このように地味な機能ではありますが、意外に便利なので、ぜひご活用ください。

*1:きっと、iPod touchも同様だと思いますが、手元には、マルチタスキング非対応のtouch 2Gしかないので、別途調査したいと思います。

iTunesのファイル共有機能を使えるようにしてみました

SysStats Monitorでは、次のバージョンで、iTunesのファイル共有機能をサポートするようにしてみました。

ファイル共有機能がどういうものなのかについては、以下の記事がわかりやすいと思います。

iWork for iOS:iTunes のファイル共有について

iPadのこととして書かれていますが、iOS4からは、iPhoneでも同じようにできますね。

アプリ側はどういうことするかというと、Info.plistの"Application supports iTunes file sharing"をYESにするだけです。これによって、iTunes経由でアプリのDocumentsディレクトリとコンピュータ間でファイルをコピーすることができるようになります。

SysStats Monitorの場合は、保存しているデータは少ないので、この機能を入れるメリットはあまりなく、試験的な意味合いで入れてみたという面も、正直なところあります。
結局のところ、再起動履歴が保存されているsqliteファイルのバックアップ/リストアが可能になるという、地味な機能追加となっています。

将来的には、もう少し何か意味のあるデータのやり取りができるようになればいいなと考えています。

ただ、1つ、気になる問題もあります。壊れたデータがリストアされてしまうと、アプリが起動できなくなったりすることもありうるわけで、ユーザのみなさんにも注意していただく必要があるかなと思います。

iPhoneの空きメモリがだんだん減って行くのはなぜか?

iOSでは、モバイルデバイスの限られたリソースや操作感を前提に、独自のマルチタスキング環境が実現されています。
ユーザから見たその特徴は、

  • アプリをすばやく切換えられる(Fast app switching)。
  • 再度呼び出した時に、前回の状態が維持されいて、操作を継続できる。

と、いったところが大きいですね。

例えば、

  • メールの中のリンクをタップしてSafariを起動して、Webサイトを見る。
  • また、メールに戻る。
  • もう一度Safariで先程見ていたものを見る。

といった一連の動作をスムーズに行えるようにするような感じです。
でも、ここまではOS3.xでもできていましたよね。

iOS4では、こういう能力を、他の標準アプリや、AppStoreで販売される一般のアプリにも広げたのだと、私は理解しています。

ちなみに、どうやってアプリを終了させるの? という質問ヘの回答は「終了させる必要はない」(スコット)「ユーザがそんなことを考える必要はないはずだ」(ジョブズ)。

iPhone OS 4はマルチタスク対応、バックグラウンドでSkypeも動作 - Engadget Japanese

上記の会話からは、「実際に終了させるべきタイミングは、OSが判断するから気にしなくていいよ。」というメッセージが伝わってきます。
でも現実には、そこまで言えるほど、うまくできあがっていないんじゃないかということを、多くのユーザは感じているのではと思います。

なぜなら、バックグラウンド化に対応したアプリのプロセスは、特定の制御条件に達するまで、終了されずに溜まって行くので、普通に使っていると、だんだん空きメモリが減って行くからです。
あと、連続使用している時間の長さとは、基本的に相関関係はなく、使い方によるところが大きいということになります。
当たり前と言えば、当たり前のことですが。。

ユーザが不満を感じるところは。。?

iOSは、アクティブなアプリが何かしようとしたタイミングで、空きメモリが足りないと判断された場合に、バックグラウンドで一時停止状態になっているアプリケーションプロセスの中で、終了させてもいいと判定されたものを強制終了するといった制御をしています。

id:paellaさんからいただいたコメント(空きメモリが少ない時に、iPhoneの中で何が起きるのか、また調べてみました。 - The iPhone Development Playground) にて、終了させてもいいという判定基準は、以下の通りとなっていることがわかりました。*1

WWDC2010のスライドで、バックグラウンドのアプリが殺される優先度として
1) 多量のメモリを使用しているSuspended状態のアプリケーション
2) 多量のメモリを使用しているRunning状態のアプリケーション
3) Suspended状態のアプリケーション
4) Running状態のアプリケーション
と記述されていますね。

この制御が、本当に空きメモリが足りなくなった時に初めて行われるという点が、ユーザに「もっさり感」を与え、不満を感じさせる原因かもしれません。

ユーザの判断で終了させたい時は、マルチタスキングバー(最近使ったアプリケーションのアイコンリスト)*2から、対象のものを選択して削除することができるのは、多くの方がご存知ですね。でも、そこにあるものがすべてバックグラウンドに存在しているとは限らないところが、残念です。そういうものを削除しても全く効果はありませんので。。

不要なプロセスは自分で選んで終了させよう - The iPhone Development Playground

PCであれば、ユーザがアプリをバックグラウンドで活かしておくか、終了させるのかを簡単に選択することができますが、iOSではちょっと面倒な操作と判断が必要です。
そこで、iPhone4になった今も、多くのユーザが、「空きメモリが足りないと判断される状況」を、自発的に作り出す「メモリ解放機能」を持ったアプリを求めるのかなと思っています。

解決するためには。。?

マルチタスキングバーで、バックグラウンドプロセスとして、存在してるかどうかを見えるようにしてくれることが、最良の解決策だと私は思います。ついでに、各プロセスのメモリ使用量が見えるようにしてくれればさらにありがたいですね。
でも、現時点ではAppleの方針とは合わないと思われるので、実現はむずかしいかもです。

では、視点を変えて、アプリ開発者側に、何かできることはあるでしょうか?

アプリの要件として、バックグラウンドで待機する必要ないのであれば、バックグラウンド化はさせないようにすることで、一定の抑止効果は得られると思います。
SysStats Monitorの場合、操作の継続性がなく、バックグラウンド化することに意味が無いので、しない設定にしています。
反対に、写真を編集するようなアプリは、編集途中で電話をうけた場合などに備えて、バックグラウンド化に対応しておくべきでしょう。

この設定はアプリ単位の静的なプロパティであり、動的に切換えることはできないので、簡単には決めにくいかもしれませんね。
(デフォルトはバックグラウンド化する設定になっているので、注意が必要ですね)

Out Of Memory Killerに似てるけど、もう少しやさしいiPhoneOSの空きメモリー制御 - The iPhone Development Playground

ということで、決定的な解決策はありませんが、まずは、現状に納得することも大事なのではと思います。

*1:"Session 105 - Adopting Multitasking on iPhone OS, Part 1"の25ページ

*2:先日(9/1)に行われたiOS4.2発表時のプレゼンで、Steve Jobs氏が "multitasking bar" と言っていたので、今後はこの用語も使用しようと思います。

自分が使っているCPU使用率とメモリ使用量を表示している理由

iPhone 4 PERFECT GUIDE』(ソフトバンククリエイティブ社刊)の201ページに、SysStats Liteを紹介していただいてます。

ソフトバンク クリエイティブ:『iPhone 4 PERFECT GUIDE』紹介アプリのリンク集

iPhone 4 PERFECT GUIDE (パーフェクトガイドシリーズ)

iPhone 4 PERFECT GUIDE (パーフェクトガイドシリーズ)

タイトルは「iPhone4」となっていますが、実際には3GSにも共通な、iOS4の新機能が一通り説明されているので、3GSユーザの方にも有意義な内容になっていると思います。
(私も、この本を読んで、GarageBandを使わなくとも、iTunesだけでオリジナル着信音を作る方法を知りました)

あと、SysStats Liteが自分のメモリ使用量とCPU使用率を表示しているという、細かい機能に注目して取り上げていただいたことが、特に、うれしいですね。
この場を借りて、御礼申し上げます。

ただし、この自分のメモリ使用量とCPU使用率を表示する機能は、他のシステム情報表示アプリケーションにはないものなので、みなさんには、あまり馴染みがないものかもしれません。ご紹介いただいたこともあり、ちょうどいいタイミングなので、これらの値の使い方を、少し詳しく説明してみたいと思います。

注: SysStats Liteとして説明していますが、いずれもSysStats Monitorにもそのまま当てはまる内容です。

CPU使用率

以下の2つの画像は、いずれも、SysStats LiteのスクリーンショットからCPU使用率の部分を抜粋したものです。
「(このアプリ)」と書かれている下の数値(仮にXとします)が、SysStats Lite自身のCPU使用率を表し、その左側の数値(仮にYとします)が、システム全体を表しています。
その場合、概ね、

Y - X ≒ [バックグラウンドプロセスのCPU使用率]

ということになります。

前者は、システム全体(Y)とSysStats Liteの使用率の数値(X)の差が小さいので、バックグラウンドプロセスのCPU使用率がのCPU使用率低めであることを示しています。

反対に後者は、差が少ないので、バックグラウンドプロセスのCPU使用率高めであることを示していますので、何か負荷の高い処理が実行されている可能性があります。ホームボタンをダブルクリックして、「最近使ったアプリケーション」を表示させ、任意のアプリを終了させることで、解消することがあります。
*1

メモリ使用量

こちらは、SysStats Liteのスクリーンショットからメモリ使用量の部分を抜粋したものです。
「このアプリの使用量」の数値は、SysStats Lite自身が使用しているメモリサイズです。

ここで、以下の2つのサイズとその合計値に着目してみてください。
(単位: MB)

空きメモリサイズ このアプリの使用量 合計
スクリーンショット前者 22.6 9.0 31.6
スクリーンショット後者 13.7 16.4 30.1

SysStats Liteは、バックグラウンドに常駐させないように設定されています。ホームボタンをクリックした時に、プロセスが終了しますので、「このアプリの使用量」は、そのときに、そのときに解放される予定のものです。
前者のケースでは、
「現在22.6MBの空きがあり、SysStats Liteが9.0MB使用しているが、SysStats Liteを終了すれば、それらの合計値(31.6MB)の空きメモリがある状態になる。」
ことが想定されます。
そう考えると、この両者の状態には、あまり差がないということが言えると思います。*2


SysStats Liteが自分のメモリ使用量とCPU使用率を表示しているのは、このように、もう少しシステムの状態を詳しく把握できるようになればいいかなと思ったことが、大きな理由です。
また、ちょっと古い話ですが、以下のような状況に遭遇したこともきっかけになっています。

待ち受け状態のiPhoneが熱くなったときの対処例 - The iPhone Development Playground

各バックグラウンドプロセスのCPU使用率とメモリ使用量が取得できれば、もちろん必要ない機能ですが、公開APIでそれらを取得する方法が見つかっていないので、このような形で実現しました。

iPhoneがもっさりしてきたと感じた時は、CPU負荷の問題であることも多いので、再起動などする前に、SysStats Monitor/SysStats Liteでも確認することで、何か原因が見つかることもあります。
興味がある方は、ぜひ、お試しください。

*1:iPhone SDKをインストールしている場合は、Instrumentsというユーティリティに内蔵されているActivityMonitorツールを使用することで、個々のプロセスのCPU使用率を見ることができます。

*2:SysStats Liteで、プロセス一覧や、ヘルプページを表示させると、「このアプリの使用量」が増加し、空きメモリが減ることがあります。