初期化処理を追加する【実践的Macintoshプログラミング解説】

印刷用表示 |テキストサイズ 小 |中 |大 |

CoreData版 Repeating Motif Generator の開発 Repeating Motif Wonderland CoreData 実践的 Macintosh プログラミング解説

LinkIconホーム

更新日 2009-05-24

原図とミキサーのモデルを作る

初期化処理を追加する

 属性で初期化が必要なものがあるので、その処理を追加します。MasterMotifはMixerを所有し、MixerはOscillatorを所有するので、awakeFromInsertで所有するオブジェクトを生成する処理が必要です。

 またawakeFromFetchでベジエパスを初期化するのですが、アンドゥの登録を無効にしていても、ある操作を実行するとベジエパスの初期化がアンドゥの対象となり、ドキュメントがダーティになってしまう事が判明しました。以前の対策は完璧ではなかったわけです。

 なぜこんな事になるのかわかりませんが、一回何らかの操作を挟むとよさそうなので、対症療法でとりあえず対策します。

awakeFromInsertで初期化を実行

 MixerCDのawakeFromInsertでやるべき事は、Oscillatorを作成し、それをoscillators対多関連に登録する事とosc1, osc2, osc3関連にセットする事です。よってMixerCDの初期化コードは以下の様になります。

 重要事項としてデベロッパドキュメントに載っていますが、サブクラスは自分の初期化コードを実行する前にスーパークラスのawakeFromInsertを呼び出さないといけません。これはawakeFromFetchでも同じです。 

// 
//  MixerCD.m
//

#pragma mark -
#pragma mark Initializer

- (void)createOscillator
{
    int i;
    OscillatorCD    *osc;

    for(i=0;i<3;i++)
    {
        osc = [NSEntityDescription insertNewObjectForEntityForName:@"Oscillator"
                                            inManagedObjectContext:MOC];
        [[self mutableSetValueForKey:@"oscillators"] addObject:osc];
        if(i==0)
            [self setOsc1:osc];
        else if(i==1)
            [self setOsc2:osc];
        else
            [self setOsc3:osc];
    }
}

- (void)awakeFromInsert
{
    [super awakeFromInsert];
    [self createOscillator];
}

 MasterMotifCDではMixerを生成します。また初期化の最後でfullUpdateを実行します。これでMasterMotif, Mixer, Oscillatorの各bezierPath属性が初期化されます。

// 
//  MasterMotifCD.m
//

#pragma mark -
#pragma mark Initializer

- (void)createMixer
{
    int i;
    MixerCD     *mixer;

    for(i=0;i<2;i++)
    {
        mixer = [NSEntityDescription insertNewObjectForEntityForName:@"Mixer"
                                              inManagedObjectContext:MOC];
        [[self mutableSetValueForKey:@"mixers"] addObject:mixer];
        if(i==0)
            [self setX:mixer];
        else
            [self setY:mixer];
    }
}

- (void)awakeFromInsert
{
    [super awakeFromInsert];
    [self createMixer];
    [self fullUpdate];
}

awakeFromFetchの謎

ファイルをオープンしただけでダーティになるで書いた様に、明示的にアンドゥの登録を禁止してベジエパスを初期化します。これでファイルをオープンしただけでダーティになる事は防げるのですが、ある操作をするとダーティになる事に気づきました。

 その操作とは「EditメニューもしくはHelpメニューを開いて、何もコマンドを実行せずに閉じる事」です。これで何らかの操作が登録されるらしく、ドキュメントがダーティになって、アンドゥメニューが有効になります。アンドゥを実行しても見た目では何も変わらず、何が実行されたのかわかりません。ところがOscillatorCDとMixerCDのawakeFromFetchをコメントアウトすると、アンドゥを実行した時にそれぞれの波形が消えます。リドゥで復活します。

 CoreDataフレームワークの内部で一体何が起こっているのでしょうか?謎です。

// 
//  MixerCD.m
//

- (void)awakeFromFetch
{
    [super awakeFromFetch];
    [UNDO_MANAGER disableUndoRegistration];
    [self updateBezierPath];
    [UNDO_MANAGER enableUndoRegistration];
}

// 
//  MasterMotifCD.m
//

- (void)awakeFromFetch
{
    [super awakeFromFetch];
    [UNDO_MANAGER disableUndoRegistration];
    [self fullUpdate];
    [UNDO_MANAGER enableUndoRegistration];
}

対症療法でとりあえず何とかする

 「EditメニューもしくはHelpメニューを開いて、何もコマンドを実行せずに閉じる事」で謎のアンドゥが実行できてしまう現象は、パラメータを変更するなど、一度何らかの操作をアンドゥマネージャに登録すると発生しません。登録した操作以上には遡れないのです。

 そこで対症療法ですが、awakeFromFetchで一度ダミーの操作を実行し、それをアンドゥしてみる事にしました。こんな感じです。

// 
//  MasterMotifCD.m
//

- (void)awakeFromFetch
{
    [super awakeFromFetch];
    [UNDO_MANAGER disableUndoRegistration];
    [self fullUpdate];
    [UNDO_MANAGER enableUndoRegistration];
    [self setResolution:[self resolution]];
    [UNDO_MANAGER undo];
}

 これで謎のアンドゥはできなくなるのですが、リドゥメニューにダミー操作をリドゥする項目が現れてしまっています。

 そこでアンドゥマネージャに登録されているアクションをリセットします。

// 
//  MasterMotifCD.m
//

- (void)awakeFromFetch
{
    [super awakeFromFetch];
    [UNDO_MANAGER disableUndoRegistration];
    [self fullUpdate];
    [UNDO_MANAGER enableUndoRegistration];
    [self setResolution:[self resolution]];
    [UNDO_MANAGER undo];
    [UNDO_MANAGER removeAllActions];
}

 これでようやく問題が解決しました。ところで、こうなってみるとアンドゥの登録を禁止、有効にしているコードはもう不要ですね。これらを削除します。

// 
//  MasterMotifCD.m
//

//  「EditメニューもしくはHelpメニューを開いて、何もコマンドを実行せずに閉じる事」で謎のアンドゥが実行できてしまう
//  パラメータを変更するなど、一度何らかの操作をアンドゥマネージャに登録すると発生しなくなる
//  対症療法だが、awakeFromFetchで一度ダミーの操作を実行し、それをアンドゥして対策とする
- (void)awakeFromFetch
{
    [super awakeFromFetch];
    [self fullUpdate];
    [self setResolution:[self resolution]];
    [UNDO_MANAGER undo];
    [UNDO_MANAGER removeAllActions];
}

 resolutionに同じ値を設定しているのだからアンドゥしなくても同じではないかと、アンドゥを実行している行も削除したところ、これはうまくいきませんでした。病状が再現します。 

 やっぱりawakeFromFetchは謎です。