2013/12/28

VMWare FusionのSCSIドライバを64bit環境用へ変更する

今までVMWare Fusion(v6.0.2)上で32bit版Windowsを動作させていましたが、64bit Windowsへアップグレードすることにしました。

OSの再インストールになりますが、OSアップグレード処理中、旧来のOSがインストールされたディスクへのアクセスが発生するため、新規VMを作成するのではなく、旧環境を更新する必要があります。

環境の変更は、メニューにある「Virtual Machine > Settings....」から「Genral」を選び、「OS」の横にあるプルダウンメニューから適切なOSを選択することで行います。

この変更を行ったVMを起動させようとしたところ、

The BusLogic SCSI adapter is not supported for 64-bit guests.

のエラーメッセージが表示され、起動できません。

この問題を修正するには、.vmx ファイルの書き換えが必要です。VMWare 社の Knowledge Base では1020879として公開されています。

.vmxファイルはVM一式と共に保存されています。

VMの保存場所は、VMWare Fusion のメニューに有る「Virtual Machine Library」から、右クリックなどでショートカットメニューを開くと表示される「Show in Finder」から確認できます。

Finder上では、単一ファイルとして表示されるため、同じくショートカットメニューの
「Show Package Contents」を選ぶと、その中に.vmxファイルが表示されます。

この中から該当するSCSIデバイス番号を親に持つ、virtualDevキーを探します。
例えばSCSIデバイス0のドライバを書き換えるにはscsi0.virtualDevを探します。

この値が"buslogic"になっている場合には、それを"lsilogic"へ書き換えます。

scsi0.virtualDev = "lsilogic"

もしこのキーが.vmxファイルへ存在しない場合には、適当な場所へ追加します。

これで、VMを64bit環境で起動できるようになります。

2013/12/02

iOS の task completion について

TMPTaskCompletionManagerの公開に伴い、task completionについてまとめてみます。

task completion は iOS4 で導入されたマルチタスク処理方式の一つです。アプリがバックグラウンドモードへ入った後も、実行中の処理を継続したいときに使います。実行可能時間は、iOS6までは最長10分間。iOS7では最長3分間へ短縮されました。

バックグラウンドでの実行要求は、
- [UIApplication beginBackgroundTaskWithExpirationHandler:]
で行います。要求は何度でも可能です。
処理が終了したなど、バックグラウンドでの実行が不要になった場合には、
- [UIApplication endBackgroundTask:]
を呼び出さなければなりません。このメソッドを呼び出すか、規定時間が経過した場合、アプリケーションはバックグラウンド処理を終了し、サスペンド状態へ入ります。

StackOverflowでも言及されているように、beginBackgroundTaskWithExpirationHandler:は、名前に「begin」と付いているものの、実際にはバックグラウンド処理を開始しません。このメソッドは、OSに対し、バックグラウンドでの処理継続を要求するために使います。

beginBackgroundTaskWithExpirationHandler:が呼び出される毎に、OSはその回数を記録します(動作可能時間は初回要求時から増えません)。この数値が1以上である場合、OSは、アプリがバックグラウンド処理を要求していると判断します。この回数は、endBackgroundTask:で減算されます。0になった時点で、OSはアプリケーションはバックグラウンド処理を望んでいないと判断します。メモリ管理のリファレンスカウントと同じ原理です。

言い換えれば、endBackgroundTask:を呼び出すまで、アプリケーションはバックグラウンド処理を要求し続けていることになります。例えば、アプリがフォアグラウンドへ復帰するなど、バックグラウンド処理が不要になった場合には、明示的にendBackgroundTask:を呼びださなければなりません。

Appleが公開しているiOS App Programming GuideExecuting a Finite-Length Task in the Backgroundでは言及されていないためか、「Cocoaの日々」「[iOS] バックグラウンド実行見本(Task Completion)」などを除いて、あまりフォアグラウンド復帰時の処理に関し、言及していないように感じています。

この動作を確認するために TaskCompletionSample を作成しました。

https://github.com/n-miyo/TaskCompletionSample

このアプリはアプリケーションバッジを1秒ごとに増加させます。beginBackgroundTaskWithExpirationHandler:の実行はviewDidLoadで行われ、endBackgroundTask:は実行されません。

アプリをバックグラウンドへ落とすと、バッジの増加を通じてバックグラウンド処理を確認できます。アプリを一度フォアグラウンドへ復帰させ、再度バックグラウンドへ遷移させてみます。この処理の過程では新たにbeginBackgroundTaskWithExpirationHandler:を呼び出していませんが、アプリはバックグラウンド実行を継続可能です。

画面の「clear at 'didBecomeActive'」スイッチをONにすると、- [AppDelegate applicationDidBecomeActive:]実行時、endBackgroundTask:を実行するようになります。
この状態で、一度アプリをバックグラウンドへ遷移させ、再度フォアグラウンドへ遷移させた後、再度アプリをbeginBackgroundTaskWithExpirationHandler:へ移行させると、endBackgroundTask:との呼び出し回数が釣り合い、以後バックグラウンドでの実行は行われません。

さて、バックグラウンド処理を要求する場合、時間切れ時に実行されるタスクを登録し、その中でもendBackgroundTask:を実行する必要があります。これらは定形で書けますが、毎回書くのは面倒なものです。

また、複数のタスクをバックグラウンド登録した場合、それらをフォアグラウンド復帰のタイミングで一斉にキャンセルするには、すべてのタスクIDを管理しなければなりません。

これらの比較的定形でありながら、毎回書くのが面倒になるコードをまとめたものが、先に公開した TMPTaskCompletionManagerになります。使い方などは、「iOS の task completion をサポートする TMPTaskCompletionManager を公開しました」を御覧ください。

ご意見などお待ちしております。

iOS の task completion をサポートする TMPTaskCompletionManager を公開しました

今更ではありますが、iOS の task completion をサポートする TMPTaskCompletionManager を公開しました。

https://github.com/n-miyo/TMPTaskCompletionManager

TMPTaskCompletionManagerは、バックグラウンドタスクの登録や、タスク終了時の登録解除処理など、定形的なコードを簡単に書くためのライブラリです。

タスクを登録するには、runBackgroundTask:taskQueue:expirationTask:メソッドで「バックグランドで実行したいタスク」と「時間切れ時に実行したいタスク」を登録します。戻り値は、バックグラウンドタスク識別子です。

「バックグラウンドで実行したいタスク」はtaskQueue:で指定したNSOperationQueue上で実行されます。nilを指定すればQueueは内部で自動生成されます。


処理が終了した場合等、バックグラウンド処理を登録解除したい場合には、cancelBackgroundTask:を使います。


フォアグラウンド復帰時など、すべての登録を解除したい場合には、cancelAllBackgroundTasks が利用できます。-[AppDelegate applicationDidBecomeActive]などで使うとよいでしょう。


各種ご意見など、お待ちしております。