So-net無料ブログ作成
検索選択
Cocoa覚え書き ブログトップ
前の10件 | -

MacOSXでのサウンドキャプチャをしてみようとする [Cocoa覚え書き]

前回、ComplexPlayThruというサンプルでリングバッファからデータを取り込めそうだということがわかった。しかし、このサンプルには音源の選択しかなくサンプリングレートが選べない。いったい、いくつでサンプリングしているのだろうかと疑問に思う。

調べてみたところ、ComplexPlayThru::SetupBuffers()の中で、AudioDeviceGetProperty()をkAudioDevicePropertyNominalSampleRateというパラメタで呼び出しているところがある。ここで取得したサンプリングレートをAudioUnitSetProperty()で出力デバイスに設定している。

kAudioDevicePropertyNominalSampleRateというのは、要はデフォルトのサンプリングレートを取得していることのようで、ComplexPlayThruのサンプルをいじって、サンプリングレートがいくつかコンソールに出るようにしてみたところ、44.1kHzであった。この値は、ハードの構成に依存するのであると思われる。

ところで入力しようとしている音源がどのサンプリングレートでキャプチャできるのかを調べるにはどうすればよいのか、ということを知りたくなる。コンバートができるようなことがドキュメントに書いてはあったが、できればハードウェアでサンプリングしたデータをそのままほしいからである。

ADC内のドキュメントをいくつかみたところ、CoreAudioのサンプルの中にあるPublicUtilityという中にあるC++のソースが載っていて、これを参考にせよとのことでした。

見てみると、kAudioDevicePropertyAvailableNominalSampleRatesというプロパティを取得するとわかるらしい、ということがわかった。そこで、AudioDeviceGetPropertyInfo()で、kAudioDevicePropertyAvailableNominalSampleRatesのサイズを取得し、AudioDeviceGetProperty()でデータを取得する。このデータはAudioValueRange構造体の配列となる。

この値もComplexPlayThruにコンソールに出るように書き換えて実行してみた。呼び出すタイミングはAudioDevice::Init()の直後で選択されている入力デバイスのID(AudioDevice::mID)で呼び出してみる。

そうすると、AudioValueRange構造体のメンバ、mMaximumとmMinimumが同じ値ではあるが、サンプリングレートらしき数値が複数格納されている。つないだ音源では、8kHzから48kHzまでのいくつかの(よく目にする)値が入っていた(当然、ハードに依存するに違いないが)。

というわけで、、kAudioDevicePropertyAvailableNominalSampleRatesでキャプチャ可能なサンプリングレートを取得して、UIに表示して選択できるようにすれば、サンプリングレートを選んでキャプチャできる、ということがわかった。


SimplePlayThruの覚え書き [Cocoa覚え書き]

SimplePlayThruサンプルのSimplePlayThru.cppのソースコードの中身を見たんですが、忘れそうなので覚え書きをしておくことにする。

SimplePlayThru::Init()

descへの代入はお決まりなようだ。
NewAUGraph()でAUGraphを作成、AUGraphNewNodeで(出力用の)ノードを作成。
AUGraphConnectNodeInput()で入力ノードに接続
AUGraphOpen()でオープン
SetupAUHAL()の呼び出し
AUGraphInitialize()で初期化


SimplePlayThru::DeviceCheck()

AudioHardwareGetProperty()で入力、出力のデバイス情報を取得
if(mOutputDevice == mInputDevice)この方法だと同一でバイスしかだめ
↑関数の上にあるコメントに書いてある


SimplePlayThru::GetDeviceName

AudioDeviceGetPropertyInfo()でプロパティサイズを取得
AudioDeviceGetProperty()でプロパティを取得、デバイス名取得


SimplePlayThru::Start()
SimplePlayThru::Stop()
SimplePlayThru::IsRunning()

それぞれ、AUGraphの関数を呼んでるだけ


SimplePlayThru::SetupAUHAL()

AUGraphGetNodeInfo()でHALUnitを取得
AudioUnitSetProperty()で入力、出力ができるように設定


Xcodeでyacc、lexを使ってみる [Cocoa覚え書き]

Xcodeには、一応?yacc、lexがサポートされていて、.y、.lの拡張子を持つファイルをプロジェクトに追加すると、デフォルトでコンパイルできる。
しかし、yacc、lexは基本的にコマンドベースのツールを作成するためのものであるため、UI付きのアプリの場合、多少問題があったりする。
それ以前に、少なくともlexを使う場合、同じことをFoundationを使ってやるのとをどっちがいいか考えてからのほうがいいかもしれない。lexも便利だがFoundationにもそれなりに機能がある。

続きを読む


Objective-Cの例外処理の方法 [Cocoa覚え書き]

Objective-Cの例外処理は、C++のtry/catch/throwとやりかたはかわらない。
○例外の取得方法(catch)
NS_DURING

 例外が発生しそうな処理

NS_HANDLER
 捕獲した例外の対処
NS_ENDHANDLER

通常、例外を捕獲しないと、そこで処理が終わってしまう(おそらく、run loopへ戻る)が、ハンドラで捕獲すると、そのまま実行し続けることもできる。

NS_HANDLERとNS_ENDHANDLERの間の例外処理には
NS_VALUERETURN();とNS_VOIDRETURN;を使ってメソッドから抜け出すことができる。

○例外の発生(throw)
NSExceptionのインスタンスを作成して、インスタンスメソッドのriseを実行する。
例えば、
[[NSException exceptionWithName:NSInvalidArgumentException reason:@"i don't like this." userInfo:nil] raise];

exceptionWithNameのところは、ユーザ定義で任意の文字列を使用することもできる。NSExceptionのクラス定義(ヘッダファイル)を見ると、既にいくつか定義されている。
詳細は、Objective-C/Exceptionのドキュメント参照のこと。

(この記事がNGワードチェッカにかからないことを祈る(笑))


Xcodeの小ネタ [Cocoa覚え書き]

小ネタですが、忘れそうなので書いておきます。
○プロジェクトへファイルの追加や新規作成の時の注意
 XcodeのプロジェクトウィンドウにあるGroups&Filesと書いてあるOutLineViewの選択位置に注意すること。表示されるウィンドウで設定可能なのだが、何も考えずにOKすると、変なところへファイルが置かれてしまう。
○実行ファイルの名前を変更する
 Xcodeのプロジェクトウィンドウでファイル名を変更してもだめで(当たり前か)、ターゲットの下にある実行ファイルを出力するためのターゲット(普通は1つだけ)のInfo PanelかInspectorを表示する(ファイルメニューのGet InfoかShow Inspectorを選択する)。
まず、Propertiesのタグのところで、Executableの項目を変更する。
次にBuildのタグのところで、「Product Name」という項目を変更する(上と同じにしておく)。
あとは、おまけでGenericのタブのところで、Name(ターゲットの名前)を変更する。
○NSApplicationをサブクラス化する
・ファイルをまだ作ってないとき
 Interface BuilderでメインのNib(デフォルトはMainMenu.nib)を開く。File's Ownerを選んで、classesのタブを選ぶと、NSApplicationが選択されているので、そこでサブクラスを作成する(classメニューかポップアップメニュで)。
 (必要ならば、OutletやActionを追加しておく。)
 作成したサブクラスのファイルを作成する(classesで作ったサブクラスを指定して、classメニューかポップアップメニューで)。
 File's OwnerのInfo Panelを開く(Toolsメニューで)。Custom Classを開くと作ったNSApplicationのサブクラスがあるので選択する。
 Xcodeに戻って、実行ファイル用のターゲットのInfo PanelかInspectorを開く。
Propertiesのタブのところで、Principal Classのところを作ったNSApplicationのサブクラス名に変更する(デフォルトはNSApplication)。
・ファイルを作ってしまったとき
 Interface BuilderでメインのNib(デフォルトはMainMenu.nib)を開く。
 Xcodeの中から作成したNSApplicationのサブクラスのヘッダーファイルをInterface Builderの開いたメインのNibのウィンドウへドラッグ&ドロップする。
そうすると、classesのタブに変わってNSApplicationのサブクラスのところにあるはず。
 あとは作ってないところと同様に、Custom ClassとPrincipal Classの変更をする。


KVCについて [Cocoa覚え書き]

本当はバイディングを書こうと思ったのだが、プログラムはそれらしく動いているもののいまいち、ふにおちないところがあるため、今回はペンディング。
Key Value Codingについては、ADCの中でも貴重な日本語ドキュメントがあるので、それを読めばだいたいわかる(はずだ)が、簡単にまとめてみると。
○基本的な考え方
 KVC準拠するということは、インスタンスがNSMutableDicrionaryにあるsetObject:forKeyやobjectForKey:に相当するメソッドを実装して、インスタンスの持つ値(value)にアクセスできるようにすることである。
 インスタンス同士で、KVCによってアクセスすることを「リレーションシップ」という。アクセスされるデータが単一の場合、「対1リレーションシップ」であり、配列に格納されているような複数の場合には「対多リレーションシップ」という。
(この対?というのは、to-one、to-manyの対訳なのだが...まぁいいか...)
 あと、プロパティなど他の用語も出てくるが、それらはADCのドキュメントを参照のこと。
○KVC準拠すると何がいいのか
 バインディングに対応できるということなのだが、KVCに準拠するとkeyの値が変更されると、参照しているインスタンスに通知される仕組みが利用できる。これは、Foundationに組み込まれており、この仕組みをKVO(Key Value Observing)という。
○実装方法(対1リレーションシップ)
<key>とset<Key>というメソッドを作成する
 <key>といっているのは、アクセスするためのkeyの名称である。<key>は最初の文字がアルファベットの小文字でなければならない。例としてnameという<key>をアクセスさせるメソッドは、-(id)nameと-(void)setName:(id)というなる(実際には「(id)」には適切な型を指定する)。
○実装方法(対多リレーションシップ)
-(unsigned int) countOf<Key>と-(id)objectIn<Key>AtIndex:(unsigned int)を作成する。

というのは、ドキュメントに書いてあることで、実際には以下のようにすればよい(と思う)。
○対1リレーションシップの場合
 いちいち<key>とset<Key>を作っていると大変な場合(いっぱいあるときとか)は、- (id)valueForUndefinedKey:(NSString *)で必要なkeyに対して値を返して、それ以外は、return [super valueForUndefinedKey:key];としてしまえばよい。
同様にsetのほうも- (void)setValue:(id) forUndefinedKey:(NSString *)を実装する。
○対多リレーションシップの場合
 上記の方法でもできなくはないが、要素ごとにアクセスしてくる場合もあるので、- (unsigned int) countOf<Key>と(id) objectIn<Key>AtIndex(unsigned int)を実装する。setのほうは、-(void)insertObject:(id) in<Key>AtIndex:(unsigned int)と-(void)removeObjectFrom<Key>AtIndexを実装する。
対1の様に -(NSMutableArray*)<key>や-(void) set<Key>:(NSArray*)でもよいかもしれない(呼ぶほうに依存しているように思われる:要調査)。
○監視(Observing)への対応
 アクセスされるほうのインスタンスは、上記のメソッド経由で値をやり取りすれば、監視へのトリガーはその延長上で発生する(Foundationがやってくれているもよう)。
○key path
 KVCでの値のアクセスする方法として、key pathといわれるものがある。これは、key1.key2のように「.(ドット)」で階層を指定して、keyにアクセスするものである。
これは、まずインスタンスのkey1にアクセスして、得られた値のインスタンスのkey2にアクセスすることである。

続きはバインディングのところで...(次期未定^^;


Objective-Cのメモリ管理がわけわからなくなったとき [Cocoa覚え書き]

あふぉな私がメモリ管理がわけわからなくなったときのための覚え書き。

○動的インスタンス生成
 C++の場合は、new 、deleteで動的なインスタンスを生成、破棄する。
 スコープ内でauto変数にnewの戻り値を代入してdeleteをするには問題ないが、newしたままスコープを出ると見失ってしまうので、クラスメンバかstaticな場所に置いておく。これは、C言語のalloc()系とfree()との関係と同じ。
 Objective-Cの場合は、alloc、releaseでインスタンスの生成、破棄をする。
スコープ外へ出るときもC、C++ともに同様である。
○基本へ戻ってC言語のauto変数
 auto記憶クラスは、スコープ内で有効である。
int foo()
{
int n=0;

return n;
}
となっている場合、関数を抜けたときに「n」は破棄されてしまうが、returnのパラメタは、関数を抜けるときにどこかへ値をコピーして、関数の戻り値となる。
ということは、関数内で動的なメモリを確保したとき、returnでそのポインタを返せば、見失うことはない。
char *newfoo()
{
char *p=malloc(128);
 …
return p;
}

char *pc=newfoo();
free(pc);
 しかしながら、呼び出し側でfree()し忘れると、見失ってしまう。

○Objective-Cのautorelease
 動的に生成したインスタンスのポインタを変数に代入したとき、その変数が破棄されるときにポインタの先のインスタンスもいっしょに破棄してもらう仕組みがautoreleaseである。
CLASS aClass;
{
aClass* anObject=[[[aClass alloc] init] autorelease];
 …
}
とした場合、スコープを抜けるとanObjectが破棄されるが、autoreleaseされているので、同時に破棄されるである(実際のところメソッドを抜けるまでと思われる)。
また、関数(メソッド)の戻り値(returnのパラメタ)となっている場合もC言語と同様にanObjectの値がどこかに保存されて、戻り値になるのでそのときには破棄されない。
+ (aClass*) method
{
aClass* anObject=[[[aClass alloc] init] autorelease];
 …
return anObject;
}

aClass* theObject=[aClass method];

コレクションクラスへの保存
 インスタンスの中に別のインスタンスを保有できる、いわゆるコンテナとなるFoundationクラスは、NSArray、NSDictionary、NSSet(サブクラスも含む)があるが、これらにインスタンスを代入したときは、そのインスタンスは参照しているものが増えたとして、参照カウンタ(retainカウンタ)を+1する。
+ (void) method:(NSMutableArray*)anArray
{
aClass* anObject=[[[aClass alloc] init] autorelease];
//この代入では参照が1
[anArray addObject:anObject];
//anArrayにオブジェクトを追加したので参照が2
}
//メソッドを抜けたあとは参照が1
 このように参照がなくなるまで(0になるまで)autoreleaseされたインスタンスは破棄されない。

○コレクションに入れておいたものをそのまま使う
 NSArrayなどに入れておいた、インスタンスをとりだしてから、それを参照している要素(もしくは保管してあるコンテナ)を破棄してしまうと、参照が0になっていっしょに破棄されてしまう。破棄してほしくない場合には、意図的に参照カウンタを増やすためにretainを呼ぶ。
aClass* anObject=[anArray lastObject];
[anArray removeLastObject];
//ここで一緒にanObjectの参照さきも破棄されてしまう。

このまま、anObjectを使いたいときには、
[anObject retain];
をremoveLastObjectを呼ぶ前に実行しておけば良い。ただし、スコープ(メソッド?)を抜けても参照カウントは減らないので、どこかでreleaseしなければならない。スコープ(メソッド?)を抜けたときに破棄したいのであれば、
aClass* anObject=[[[anArray lastObject] retain] autorelease];
[anArray removeLastObject];
とすればよい。


NSSortDescriptorを使ったソートとNSTableViewでソートする方法 [Cocoa覚え書き]

NSSortDescriptorの使い方ですが、ドキュメントを読めば大方理解できると思いますが、念のため。
○対象となるデータ
 NSDitctionaryを全要素に含むNSArray
○NSSortDescriptorの作成とNSArrayへの格納
 NSSortDescriptorのインスタンス作成時のkeyとは、対象データ(上記のNSArray)に含まれるNSDictionaryのkeyを示す。
 すなわち、NSDictionaryにあるデータのうち、このkeyで格納されているもの(objectForKeyで得られるデータ)をソートすることである。
比較メソッド(compare:など)は、比較しようとするデータに実装されているメソッドを指定する。NSStringやNSDateなどはあらかじめが実装されているcompareでよい。
 ここで作成したNSSortDescriptorは、NSArrayにいれる(1つだけのときも)
○ソートの実行
 対象となるNSArrayのsortedArrayUsingDescriptors:を使って、作成したNSSortDescriptorを含むNSArray渡す。ソートの結果は戻り値のNSArrayに格納される。

NSTableViewでのソートのやりかた
 ここでは、datasourceを使ってNSTableViewのソートの方法についてですが、使わなくても、ほぼ変わりはないでしょう。
○Interface Builderでの定義
 Interface BuilderでNSTableViewのカラムを選択すると、ソートの定義がInfo Panelに表示される。
 このパネルで、keyと比較メソッドと昇順、降順の指定をする。
実際にNSTableViewが動作しているときにカラムヘッダをクリックすると、この値を使ってNSTableViewがNSSortDescriptorを作成する。
○ソートの実行
 datasouceを使う場合、
tableView:objectValueForTableColumn:row:の呼び出されることにより、NSTableViewへデータを送るのだが、少なくともそのまえに
NSTableViewのsortDescriptorsメソッドでNSSortDescriptorを取得して、ソートを実行する。
表示されているNSTableViewのカラムヘッダにソートのインジケータ(上、下向き三角)が表示されていれば、sortDescriptorsメソッドは、Interface Builderで設定した値で作成されたNSSortDescriptorを要素に持つNSArrayを返す。(インジケータがないときは、NSArrayの要素は0個)。
このNSArrayを使って表示しようとしているデータをソートする。
 すなわち、NSTableViewでこの仕組みを利用して、ソートを使う場合には、プログラム内部で持っているデータは、NSDictionaryを要素に持つNSArrayを用意しておかなければならない。
Interface Builderでソートの設定で指定するkeyは、内部データ構造上のkeyを指定するということである。
○カラムをクリックしたときの再表示
 NSTableViewにソート指定されている場合には、ソートのインジケータが表示される。カラムヘッダがクリックされた場合、インジケータの方向が変わる。
 このとき、datasouceをしてしている場合、インジケータが変わった直後にtableView:sortDescriptorsDidChange:が呼び出されるので、NSTableViewのreloadDataを呼び出す(この結果、再表示が実行される)。渡される引数のNSSortDescriptorは特に気にする必要はない(次のNSTableViewのsortDescriptorsで得られるNSSortDescriptorは、ビューと連動してに変わっている)。


MSC(MFC)からObjective-C(Cocoa)へのSwitch&Quick Start [Cocoa覚え書き]

とりあえず、何かやってみようと思ったときの覚え書き。
○まずは
@で始まる記述がObjective-Cの固有なもの
○クラス定義と実装
@interface クラス名:親クラス名
{
変数定義
}
メソッド定義
@end
ですが(プロトコル定義もあるがここでは省略)、ヘッダに記述するメソッド定義は、publicかprotectedだけでよい。実装ファイル(.mファイル)には、
@interface クラス名
メソッド定義
@end
を書いておくとprivateな定義になる(というよりも、C言語的プロトタイプ宣言様な感じ)。
メソッドの実装は
@implementation クラス名
メソッドの実装
@end
○変数定義
ここで変数定義といっているものは、インスタンス変数(C++ でのobj.xやptr->xでアクセスするもの)である。いわゆるクラス変数はないもよう。あえていうならば実装ファイル内でC言語的に
static 型 変数名(=初期値);
と@implementation@endで囲まれていないところで書く。
Objective-Cのオブジェクトは動的に生成するので、ポインタでの宣言しかない(と思う)。ただし、C言語のstructで定義されるものは、実体で定義できる。
○メソッド定義
定義の最初に「+」「-」があるがC++でいうところのstaticクラス関数が「+」で、staticでないものが「-」
あとは、戻り値がきて、メソッド名だが
-(id) method が引数なし
-(id) method:(id)arg が引数1個
-(id) method:(id)arg option:(id)arg2 が引数2個
で以降はoption:argの形式が 増えていくだけ。ちなみに「id」という識別子は、オブジェクトへのポインタ(void*なようなもの)。
○メソッドの呼び出し
Objective-Cではメソッドの呼び出しを「メッセージを送る」というらしい。
[object method]; [object method:arg];  [object method:arg option:arg2];
の様にして呼び出すと「-」で定義したメソッドが呼ばれる(C++での「ptr->foo()」と同じ)、
[class名 method]; の様にすると、「+」で定義したメソッドが呼ばれる(C++での「Class::foo()」と同じ。
なお、C++の場合同一のNamespace内での関数を指定するときに自己参照を省略できたが、Objective-Cでは、省略できない。すなわち、C++で
class Class{
foo();
foo2();
};
という定義のもとで
Class::foo(){
foo2();
}
と書くとfoo2()は、this->foo2()の意味であるが、Objective-Cでは
[object method];
の形式で呼ばなくてはならないため、同じクラスのメソッド呼び出しでも以下のように書かなくてはならない。
[self method];
○オブジェクトの生成
オブジェクトは動的生成するしかないのだが、生成するには、
[class alloc] とすればよいallocはC++でのnew演算子と同じだが、そのあとにinitを呼ばなくてはならない。
[object init] 引数なしの場合
[object initXXX:arg] 引数がある場合
のように普通のメソッド呼び出しと同じようにする。初期化のためのメソッドは引数がある場合、メソッド名は必ずinitで始まる。
allocは生成したオブジェクトのポインタを返すので、以下のように書ける。
[[class alloc] init]
○初期化と後始末
-(id) init または -(id) initXXX:(id)xxx
{
初期化したいもの
return self;
}
と普通は書くが、初期化に失敗したらselfをリリースして「nil」(C++のNULLと同じ)を返すことができる。あと「self」は自己参照ポインタ(C++の「this」と同じ)。
-(void)dealloc
{
後始末したいもの
}
C++のデストラクタ同様にdeallocを直接呼ぶことはない(呼んではいけない)。
○オブジェクトの破棄
[object release]
でオブジェクトを破棄する。ただし、プログラム上でリリースするオブジェクトは、自分がallocしたものかcopyしたものかretainしたものだけで、身に覚えのないものはリリースしてはいけない。
リリースとしてautoreleaseというものがある。これは、そこでは破棄されないがリリースするものである。例えば
-(id)method
{
id object=[[class alloc] init];
return [object autorelease];
}
とした場合、このメソッド内ではリリースしたことになるが、オブジェクトは破棄されない。別のメソッド内で
id object=[self method];
と参照された場合には、このオブジェクトは有効であり、利用することができる(autoreleaseをreleaseとしてしまった場合、参照エラーとなる)。
そして、この参照している変数がブロックをでたり、関数を抜けるなりすると自動的に破棄される。
しかし、さらにインスタンス変数に代入するなどで、ブロックや関数を抜けても使い続けたい場合には、「retain」を使う。ただし、自分でリテインしたオブジェクトは、自分でどこかでreleaseもしくはautoreleaseしないとメモリリークする。
○クラスメソッドでのオブジェクトの生成
クラスの一部にクラスメソッドでオブジェクトを作るものがある。例えば
NSMutableString* string=[NSMutableString string];
この場合、このオブジェクトはリリースする必要はない。すなわち以下と等価である。
[[[NSMutableString alloc] init] autorelease]
コンテナオブジェクトとリリース
いわゆるコンテナオブジェクト、NSArray,NSDictionary,NSSet(それぞれ、Mutabaleも含む)にオブジェクトを入れると、コンテナオブジェクトが破棄されるときにいっしょに中身のオブジェクトも破棄される。例えば
NSMutableArray* array=[NSMutableArray array];
[array addObject:object];
とした場合、arrayの参照がなくなるといっしょにobjectもリリースされる。いっしょに破棄してほしくない場合には、どこかでリテインすればよい。例えば、
[array addObject:[object retain]];
こうすると、arrayがいなくなってもobjectは使い続けることができる。
○おまけ
ーSEL型
提供されているクラスの中にSELという型を渡すものがある。これはセレクタといわれているメソッド名を渡すものである(C++だと関数ポインタを渡すこと)。
この型にするためには「@selector()」を使う。例えば、
@selector(method) 引数がない場合
@selector(method:) 引数が1つの場合
ー文字列(NSString)の定数
MFCのCStringは、operator=などでLP(C)STRと互換がとれているので、
CString str="string";やCString* str=new CString("string");
という記述が可能であるが、Objective-Cでは""で指定された文字列はCStringをいう扱いになる。そのため、NSStringに代入するための文字定数は@""の形式で代入することになる。例えば、
NSString* string=@"string";
[string isEqualToString:@"string"];
というように、@""の型はNSString*になる。
ープロトコル
最初に省略してしまったがプロトコルだが、C++でいうと純粋仮想クラスと同じである。以下は文法的にはすべてではないが、とりあえず、これだけ知っていればなんとかなる。定義は
@protocol プロトコル名
メソッド定義
@end
変数としての定義
型 < プロトコル名 > 変数名;
例えば、
id < protocol> object;
クラスがプロトコルに対応していることを指定するには
@interface class クラス名 :親クラス名 < プロトコル名 >
@end
とクラス定義する。例えば
@interface MyClass : NSObject< Protocol>
@end


NSDocumentControllerを介さず、Documentを作成する方法(たぶん) [Cocoa覚え書き]

NSDocumentoController へデレゲートされる NewDocument や OpenDocument を呼ばずにして、いわば無理矢理 NSDocument を作成して、 NSDocumentController に登録する方法(ただし、完全に検証できてません。だめなところがあったらご指摘を)。
○ NSDocument のサブクラスのインスタンス作成
NSDocument のサブクラスを alloc して、 initxxx で作成しても良さそうだが、 NSDocumentControllerのmakexxx を使って、インスタンスを作成する。
例えば:
MyDocument document=[NSDocumentController sharedDocumentController] makeDocumentWithContentsOfFile:fileName ofType:@"MyDocument"];
 Typeは、 Info.plist に登録してある、 DocumentType を指定する。
○作成したインスタンスの NSDocumentController を作成する
  makeWindowControllers を呼び出す。
例えば:
 [document makeWindowControllers];
○ NSDocumentController に作成した document を追加する
例えば:
 [[NSDocumentController sharedDocumentController] addDocument:document];

ちなみに普通はこんなことしなくても良いです。ドキュメントターゲットとして、ローカルなファイルが指定できない場合にこうするしかなかったのでやってみました。
ただし、作ったプログラムでは、表示されたウィンドウのタイトルにドキュメントアイコンが表示されませんでした(nibとの関連づけの問題?)。
きっと、そのうちもっといい方法が思いつくに違いない...


前の10件 | - Cocoa覚え書き ブログトップ
Copyright © 2005-2009, Finky All Rights Reserved.

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。