NSValueTransformer

ADC *1をさまよっている時に OCUnit という単語が目に付きました。興味があったので ADC で検索し

Test Driving Your Code with OCUnit
http://developer.apple.com/tools/unittest.html

ページを見つけました。解説のサンプルとして

/Developer/Examples/AppKit/TemperatureConverter

を利用しています。

元のTemperatureConverter を変更したくないので TemperatureConverter フォルダをコピーして適当な場所にペーストし、動作を確認しました。

OCUnit を試すつもりだったのですが、TemperatureConverter を理解しないと先に進めません。(今回は時間がなく OCUnit は試すことができませんでした。 )

そこでソースを覗いてみました。NSValueTransformer を利用しています。私は NSValueTransformer をよく理解していませんでした。ソースや Interface Builder でバインディングを調べてみるとだいたい理解できました。

確認のために

Value Transformer Programming Guide
http://developer.apple.com/documentation/Cocoa/Conceptual/ValueTransformers/index.html#//apple_ref/doc/uid/10000175i
NSValueTransformer Class Reference (Objective-C)
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSValueTransformer_Class/index.html#//apple_ref/doc/uid/TP40003766

を読みました。TemperatureConverter の動作を理解した後なので、わかりやすかったです。

ただ Available Value Transformers の

NSNegateBooleanTransformerName
NSIsNilTransformerName
NSIsNotNilTransformerName
NSUnarchiveFromDataTransformerName

をどう利用すれば良いのか理解できませんでした。

Xcodeフレームワーク内を NSNegateBooleanTransformerName 検索してみると

/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSValueTransformer.h
FOUNDATION_EXPORT NSString * const NSNegateBooleanTransformerName AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER;

と宣言されています。

NSLog(@"%@", NSNegateBooleanTransformerName); 

を実行してみると NSNegateBoolean と表示されます。NSNegateBooleanTransformerName の指す文字列 “NSNegateBoolean” は Interface Builder の Inspector/Bindings/ValueTransformer で選択できる文字列と同一のようです。

const の説明は

Const-correctness - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Const

を参考にしてください。わかりやすく説明されています。

TemperatureConverter の動作を記しておきます。(間違っている可能性もあるので注意してください。)

1. + (void) initialize

クラス ApplicationDelegate が初期化される時に呼び出されるメソッド + (void) initialize で3つの ValueTransformer

Name: centrigradeFromKelvin : CentigradeValueTransformer
Name: fahrenheitFromKelvin : FahrenheitValueTransformer
Name: rankineFromKelvin : RankineValueTransformer

を設定しています。クラスの初期化時に名前を指定して自作のValueTransformerを設定すると良いようです。指定した名前は Interface Builder で利用できます。

2. 伝搬 (簡略化されています。)

TemperatureConverter では ウインドウに表示されている4つ (ケルビン : Kelvin、摂氏: Centigrade、カ氏 : Fahrenhei、ランキン温度 : Rankine) のテキストフィールド (本当はNSFormCell) がすべて LastTemperature にバインドされています。LastTemperature の温度単位はケルビンです。

例えば Centigrade テキストフィールドの値を変更すると

Centigrade テキストフィールドの
ValueTransformer (CentigradeValueTransformer) – (id)reverseTransformedValue:(id)value;
が呼び出され、Kelvin に変換され、LastTemperature に設定される。

LastTemperature が変更されたため、利用している4つ (Centigrade 、Kelvin、Fahrenhei、Rankine) のテキストフィールドに通知される。

Kelvin テキストフィールドは伝わった値を – (void)setObjectValue:(id)object; でテキストフィールドに設定し表示。

Centigrade、Fahrenhei、Rankine テキストフィールドはそれぞれの ValueTransformer (CentigradeValueTransformer、FahrenheitValueTransformer、RankineValueTransformer) の – (id)transformedValue:(id)value; で変換され、その値を – (void)setObjectValue:(id)object; でテキストフィールドに設定する。 – (id)transformedValue:(id)value; の value が LastTemperature。

Cocoa Bindings Programming Topics: Bindings Message Flow
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/MessageFlow.html#//apple_ref/doc/uid/TP40002149

が参考になります。

*1:Apple Developer Connection [ http://developer.apple.com/ ]

HelloKernel.kext、HelloIOKit.kext

Framework Programming Guide
http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/index.html

以前[ id:hatenadiary:20070205 ] にも書いた Framework Programming Guide を読み終えました。まだ完全には理解していませんが、今度は無謀にもデバイスドライバのドキュメントに挑戦してみました。

まず最初に

Getting Started with Hardware and Drivers: Getting Started with Hardware & Drivers
http://developer.apple.com/referencelibrary/GettingStarted/GS_HardwareDrivers/index.html

を読みました。ここで紹介されていた

I/O Kit Fundamentals
http://developer.apple.com/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/index.html#//apple_ref/doc/uid/TP0000011

を読み始めたのですが、理解しづらいので Getting Started with Hardware and Drivers で紹介されていた下記 URL を参考にデバイスドライバのサンプル Hello Kernel、Hello IOKit を作成してみました。

Kernel Extension Concepts
http://developer.apple.com/documentation/Darwin/Conceptual/KEXTConcept/index.html

ターミナルでコマンド操作を理解していて、Xcode でプログラム経験があれば、何の問題もなく Hello Kernel、Hello IOKit を作成できると思います。

下記に表されているのが、実際にドライバをそれぞれ読み込んで kextstat を使って表示した状態です。

~ yoshiaki$kextstat
Index Refs Address    Size       Wired      Name (Version) <Linked Against>
99    1 0xa58000   0x42000    0x41000    com.apple.driver.AppleOnboardAudio (2.5.6b5) <98 71 29 11>
101    0 0xb39000   0x6000     0x5000     com.apple.driver.AppleOnyxAudio (2.5.6b5) <99 71 11>
103    0 0x9b8000   0xb000     0xa000     com.apple.iokit.IOBluetoothSerialManager (1.7.8f2) <93 11>
104    0 0x302b9000 0x2000     0x1000     com.MyTutorial.kext.HelloKernel (1.0.0d1) <11>
105    0 0x302c3000 0x3000     0x2000     com.MyTutorial.driver.HelloIOKit (1.0.0d1) <11>
Kernel Extension Concepts: Hello Debugger: Debugging a Device Driver With GDB
http://developer.apple.com/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/hello_debugger.html#//apple_ref/doc/uid/20002367

Kernel Extension Concepts: Hello Debugger: Debugging a Device Driver With GDB を試してみたかったのですが、デバイスドライバGDB で Debug する環境がありませんでした。

Kernel Extension Concepts: Packaging Your KEXT for Distribution and Installation
http://developer.apple.com/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptPackaging/packaging_kext.html#//apple_ref/doc/uid/20002368

Kernel Extension Concepts: Packaging Your KEXT for Distribution and Installation を参考に Hello IOKit を PackageMaker を使って、パッケージ (HelloIOKit.pkg) にしてみました。

PackageMaker の利用は難しいものとばかり思っていたのですが、以外と簡単で驚きました。xshogi.app に利用してみたいですね!

HelloIOKit.pkg をインストールし再起動した後(起動に少し時間がかかりました。)、 kextstat で表示した結果が次の通りです。

~ yoshiaki$kextstat
Index Refs Address    Size       Wired      Name (Version) <Linked Against>
96    0 0x30d61000 0xa000     0x9000     com.apple.iokit.IOFireWireIP (1.3.4) <37 25 6 5 4 3 2>
97    0 0x30d6b000 0x5000     0x4000     com.apple.driver.InternalModemSupport (2.3.6) <89 29 11>
98    0 0x30d70000 0x3000     0x2000     com.MyTutorial.driver.HelloIOKit (1.0.0d1) <11>
99    0 0x30d73000 0xd000     0xc000     com.apple.driver.AppleTexasAudio (2.5.6b5) <87 86 85 84 11>
101    1 0x30d8c000 0x2b000    0x2a000    com.apple.driver.AppleDBDMAAudio (2.5.6b5) <84 11>
102    1 0x32559000 0x42000    0x41000    com.apple.driver.AppleOnboardAudio (2.5.6b5) <101 84 29 11>
103    0 0x30db7000 0x6000     0x5000     com.apple.driver.AppleOnyxAudio (2.5.6b5) <102 84 11>
104    0 0x3270a000 0xb000     0xa000     com.apple.iokit.IOBluetoothSerialManager (1.7.8f2) <89 11>

Hello IOKit のアンインストールするシェルスクリプトも作成しました。基本的には

kextunload HelloIOKit.kext
rm -R /System/Library/Extensions/HelloIOKit.kext
rm -R /Library/Receipts/HelloIOKit.pkg

を実行しているだけです。この辺はあまり詳しくないので注意して実行してください。

ターミナル上で実行する gnushogi の Universal Binary 化に失敗

私がパッケージ化して公開している xshogi.app を Universal Binary 化する前に、gnushogi の Universal Binary 化に挑戦してみました。

とりあえず

Building an Open Source Universal Binary
http://developer.apple.com/opensource/buildingopensourceuniversal.html

を読んでみました。

ここでは Xcode を利用して説明されていますが、PPC、Intel 版の gnushogi をそれぞれ作成し、コマンド lipo を使って Universal Binary 化すれば良さそうです。実際試してみました。私の環境は iMac G5 なので、もちろん PPC版の gnushogi は作成できます。Intel版の gnushogi を作成するために make を使って、コマンド make “CC=cc -arch i386” 実行したのですが、ライブラリのリンクに失敗します。いろいろ試してみたのですが、残念ながら解決することはできませんでした。

話は変わって 、Building an Open Source Universal Binary の説明に Xcode で New Run Script Build Phase を利用していますが、そこで次のような Shell Script を設定しています。

Listing 4: The Run lipo Script
# shell script goes here
for lib in `cat libnames.tmp`; do
lipo -create build/*/$lib -output $lib
done
exit 0

私自身 shell script の文法 (for 文など)*1については理解していなかったので、ADC *2で検索してみました。

本当にわかりやすく説明されているドキュメントを見つけたので URL を下記に記載しておきます。

Shell Scripting Primer
http://developer.apple.com/documentation/OpenSource/Conceptual/ShellScripting/index.html

*1:[http://developer.apple.com/documentation/OpenSource/Conceptual/ShellScripting/index.html:title=Shell Scripting Primer] : Shell Scripting Primer/Shell Script Basics/Basic Control Statements/The for Statement

*2:Apple Developer Connection – http://developer.apple.com/