2012年4月28日土曜日

Macの調子が悪いときに試すこと

ディスクユーティリティでアクセス権の修復

そもそもアクセス権(パーミッション)が意図せず変更されてしまう状況というのがよくわからないのだけど、これはMacの世界では常識だそうで、実際にやってみるとアクセス権のエラーが報告されることは多々ある。これによってどんな問題が解決するのかもよくわからないけど、とにかく真っ先に試すべきことらしい。

仕組みは単純で、アップル製のインストーラによって作られたファイルはすべてインストール時のアクセス権が記録されていて、その通りに戻しているだけ。ということで、サードパーティのインストーラや手動でコピーされたファイルのアクセス権は修復できない。

ディスクの検証と修復

ディスクユーティリティによるディスクの検証は、OS Xを起動している状態でも可能だけど、修復は選択できないようになっている。修復を行う最も簡単な方法はセーフモードで起動(セーフブート)すること。セーフブートを行うにはMacの電源を入れた直後からshiftキーを押し続ける。起動の過程で自動的にディスクの修復が行われる。

NVRAM/PRAMのリセット

Macでは、ロジックボード(マザーボード)上にNVRAMもしくはPRAMという不揮発性メモリがあって、システムやデバイスの設定が保存されている。保存されるのは、

  • スピーカーの音量
  • 画面の解像度
  • 起動ディスクの選択

などだそうで、これらの機能に関連する問題が発生したらNVRAM/PRAMのリセットを試す。起動と同時にcommand + option + P + Rキーをすべて同時に押し、2度目の起動音が聞こえるまで押したままにする。

Apple Hardware Test

Apple Hardware Testはハードウェアに問題がないか調べるソフトウェアで、Macに内蔵されていたり、インターネットから自動的にダウンロードされたり、インストールディスクから起動する必要があったりする。Apple Hardware Testを使うには、Macの電源を入れた直後からDキーを押し続ける。

SMC/PMUのリセット

SMC(システム管理コントローラ)はロジックボード上にあるコントローラチップ。電源やバッテリ、ファン、ライト/ランプ関連の制御を行っていて、それらに問題が発生したときにリセットが必要になることがある。別の問題を引き起こす場合もあるそうなので、アップルは他のすべてのトラブルシューティングを行い、それでも解決しない場合のみ試すように奨めている。詳細はリンク先を参照。

古い機種ではPMU(パワーマネジメントユニット)という名前だったらしい。

2012年4月9日月曜日

NSUserDefaultsとmutable deep copy

NSUserDefaultsにmutableなオブジェクトを保存しても、取り出すとimmutableなオブジェクトが返る、とクラスリファレンスには書いてある。

Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value. For example, if you set a mutable string as the value for "MyStringDefault", the string you later retrieve using stringForKey: will be immutable.

実際にはmutableなオブジェクトが返されることもあるようだけど、その挙動が保証されているわけではない。ということで、mutableなオブジェクトが必要な場合は-mutableCopyなどを使って作り直す必要がある。

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* addresses = [[[userDefaults arrayForKey:@"Addresses"] mutableCopy] autorelease];

NSMutableArrayがさらにNSMutableDictionaryを含んでいるような場合、-mutableCopyはshallow copyなので使えない。-mutableCopyのdeep copy版があればいいのだけど、そんな便利なものはFoundationにはなくて、Core FoundationのCFPropertyListCreateDeepCopyという関数を使う。

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* contacts = (NSMutableArray*)CFPropertyListCreateDeepCopy(NULL,
                                                                         (CFArrayRef)[userDefaults arrayForKey:@"Contacts"],
                                                                         kCFPropertyListMutableContainersAndLeaves);
[contacts autorelease];

返り値と第2引数(コピーするオブジェクト)の型はCFPropertyListRefで、これはプロパティリストオブジェクトの抽象型ということになっている。つまり、CFData, CFString, CFArray, CFDictionary, CFDate, CFBoolean, CFNumberのスーパークラスのようなもの。さらに、これらの型は対応するFoundationのクラスとtoll-free bridgedなので、結局、キャストするだけで対応するFoundationのクラスを渡したり受け取ったりできる。

第3引数ではどこまでmutableにするかを指定する。kCFPropertyListMutableContainersAndLeavesを指定すると、コンテナ(CFArrayCFDictionary)が含む末端のオブジェクトもすべてmutableになる。一方、kCFPropertyListMutableContainersの場合は末端のオブジェクトはimmutableになる。

CFPropertyListCreateDeepCopyは、NSUserDefaultsと関係なく汎用的に使うことももちろんできる。しかし、プロパティオブジェクトしかサポートしていないため、例えばNSSetなどが含まれているとエラーになってしまう。