アクセサメソッドを修正する【実践的Macintoshプログラミング解説】

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

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

LinkIconホーム

更新日 2009-05-24

モデルを作る

アクセサメソッドを修正する

generateAccessor.png 生成されたアクセサメソッドを短く書き直します。

ユーティリティメソッドを使って書き直す

 ソースコード生成時に「アクセサを生成」にチェックを入れておいたので、アクセサメソッドが生成されています。例えばamplitudeのアクセサメソッドはこうなっています。

- (NSNumber *)amplitude
{
    NSNumber * tmpValue;

    [self willAccessValueForKey:@"amplitude"];
    tmpValue = [self primitiveValueForKey:@"amplitude"];
    [self didAccessValueForKey:@"amplitude"];

    return  tmpValue;
}

- (void)setAmplitude:(NSNumber *)value
{
    [self willChangeValueForKey:@"amplitude"];
    [self setPrimitiveValue:value forKey:@"amplitude"];
    [self didChangeValueForKey:@"amplitude"];
}

 他のアクセサメソッドもキー文字列が違うだけで、中身はあまり変わりません。たった一つのプロパティに対してこれですから、プロパティの数が増えてくると、いたずらにソースコードの量が増えて見通しが悪くなりそうです。Objective-C 2.0だとこの辺りが解決されているのですが、Tigerもサポートしたいので2.0は使えません。

 そこでOscillatorCDのスーパークラスにユーティリティメソッドを用意する事で、この問題を解決する事にします。スーパークラス(親エンティティ)の名前をRMGRootとします。

OscillatorEntityList2.png モデルエディタに戻ってRMGRootエンティティを追加します。属性も関連も追加しませんが「抽象」チェックボックスにはチェックを入れておきます。
 そしてOscillatorエンティティの親としてRMGRootを指定します。

 先ほど作成したOscillatorCD.hとOscillatorCD.mを削除して、再度新規ファイルを作成します。RMGRootとOscillatorを両方選択しておきます。


newFileAssistant4.png

 

Getter作成をサポートするユーティリティメソッド

 RMGRoot.mに以下のメソッドを追加します。

 内容的には「アクセサ生成」でテンプレートにより作成されたメソッドのキー文字列が引数になっているだけです。

// 
//  RMGRoot.m
//

//  Getterを一行で書くためのメソッド
- (id)rmgValueForKey:(NSString *)key
{
    id  tmpValue;

    [self willAccessValueForKey:key];
    tmpValue = [self primitiveValueForKey:key];
    [self didAccessValueForKey:key];

    return tmpValue;
}

 インターフェイスファイルにメソッドの宣言を追加します。より簡潔に記載するためのマクロも用意します。VFKはValue For Keyの略です。

//
//  RMGRoot.h
//  RepeatingMotifGenerator
//
//  Copyright 2008 NovemberKou. All rights reserved.
//

#import <CoreData/CoreData.h>


#define     VFK(key)    [self rmgValueForKey:@key]

@interface RMGRoot :  NSManagedObject  
{
}

- (id)rmgValueForKey:(NSString *)key;

@end

【失敗例】引数付きマクロの引数と二重引用符


 最初はVFKという引数付きマクロは

#define     VFK(key)        [self rmgValueForKey:@"key"]

と定義していました。ところがビルドして実行してみると、ウィンドウが表示されず何やらうまくいっていない様です。
 「実行」メニューの「コンソール」を実行してデバッガコンソールを表示してみると、案の定何やらエラーメッセージが表示されています。keyというキーはないぞと言っている様です。
 そこで「ビルド」メニューの「プリプロセス」を実行してみるとVFK(amplitude)が[self rmgValueForKey:@"key"]と展開されている事がわかりました。
 そうとわかれば

#define     VFK(key)        [self rmgValueForKey:@key]

に修正して、使う時はVFK("amplitude")として解決です。引数付きマクロは文字列を扱うので""で囲ってはダメという事でしょうか?

Setter作成をサポートするユーティリティメソッド

 次はSetterの方です。これもテンプレートで作成されたメソッドのキー文字列を引数にすればよいのですが、それに加えてアンドゥメニュー項目に現れるアクション名の設定もできる様にします。

- (void)setActionName:(NSString *)actionName
{
    if(![UNDO_MANAGER isUndoing] && ![UNDO_MANAGER isRedoing])
        [UNDO_MANAGER setActionName:NSLocalizedString(actionName,nil)];
}

//  アンドゥのアクション名を同時にセットするSetter
- (void)setValue:(id)value
          forKey:(NSString *)key
          action:(NSString *)actionName
{
    [self willChangeValueForKey:key];
    [self setPrimitiveValue:value forKey:key];
    [self didChangeValueForKey:key];
    if(actionName != nil)
        [self setActionName:actionName];
}

 ここでUNDO_MANAGERはマクロで定義します。よく使うのでインターフェイスファイルに記述しておきましょう。メソッドの宣言も追加すると、以下の様になります。

//
//  RMGRoot.h
//  RepeatingMotifGenerator
//
//  Copyright 2008 NovemberKou. All rights reserved.
//

#import <CoreData/CoreData.h>


#define     VFK(key)        [self rmgValueForKey:@key]
#define     UNDO_MANAGER    [[self managedObjectContext] undoManager]

@interface RMGRoot :  NSManagedObject  
{
}

- (id)rmgValueForKey:(NSString *)key;
- (void)setValue:(id)value
          forKey:(NSString *)key
          action:(NSString *)actionName;

@end

Oscillatorのアクセサメソッドを修正する

 これで準備が終わりました。OscillatorCDに戻ってアクセサメソッドを修正します。色々と準備したおかげで、以下の様に簡潔に記述できます。

// 
//  OscillatorCD.m
//  RepeatingMotifGenerator
//
//  Copyright 2008 NovemberKou. All rights reserved.
//

#import "OscillatorCD.h"


@implementation OscillatorCD 

#pragma mark -
#pragma mark Accessor methods

- (NSNumber *)amplitude         {return  VFK("amplitude");}
- (NSNumber *)frequency         {return  VFK("frequency");}
- (NSNumber *)phaseLag          {return  VFK("phaseLag");}
- (NSBezierPath *)waveform      {return  VFK("waveform");}

- (void)setAmplitude:(NSNumber *)value
{
    [self setValue:value forKey:@"amplitude" action:@"setAmplitude"];
}

- (void)setFrequency:(NSNumber *)value 
{
    [self setValue:value forKey:@"frequency" action:@"setFrequency"];
}

- (void)setPhaseLag:(NSNumber *)value 
{
    [self setValue:value forKey:@"phaseLag" action:@"setPhaseLag"];
}

- (void)setWaveform:(NSBezierPath *)value 
{
    [self setValue:value forKey:@"waveform" action:nil];
}

@end

 ユーザが直接設定しないwaveform属性のSetterではアクション名を設定する必要がないのでnilを渡しています。

 インターフェイスファイルを再生成したので、先ほどUNKNOWN_TYPEをNSBezierPath *に修正した分が消えています。再度修正すると以下の様になります。

 親エンティティを指定したのでOscillatorCDクラスのスーパークラスがRMGRootに変わっています。また、そのインターフェイスファイルがインポートされています。

//
//  OscillatorCD.h
//  RepeatingMotifGenerator
//
//  Copyright 2008 NovemberKou. All rights reserved.
//

#import <CoreData/CoreData.h>
#import "RMGRoot.h"


@interface OscillatorCD :  RMGRoot
{
}

- (NSNumber *)amplitude;
- (void)setAmplitude:(NSNumber *)value;

- (NSNumber *)frequency;
- (void)setFrequency:(NSNumber *)value;

- (NSNumber *)phaseLag;
- (void)setPhaseLag:(NSNumber *)value;

- (NSBezierPath *)waveform;
- (void)setWaveform:(NSBezierPath *)value;

@end