3

私はしばらくの間 Objective-C を使用してきましたが、Apple のガイドラインにはあまり従いませんでした。最近、私はCocoa Design PatternsModel Object Implementation Guideを読み、非常に単純なことをいくつかやろうとしていますが、うまくやっています。

主要な概念を見逃していませんか? 言及しないでくださいself = [super init]; それはすでにSOで何度も取り上げられています。ただし、私のものを自由に批評して#pragma markください!

#import "IRTileset.h"
#import "IRTileTemplate.h"

@interface IRTileset () //No longer lists protocols because of Felixyz

@property (retain) NSMutableArray* tileTemplates; //Added because of TechZen

@end

#pragma mark -
@implementation IRTileset

#pragma mark -
#pragma mark Initialization

- (IRTileset*)init
{
    if (![super init])
    {
        return nil;
    }

    tileTemplates = [NSMutableArray new];

    return self;
}

- (void)dealloc
{
    [tileTemplates release];
    [uniqueID release]; //Added because of Felixyz (and because OOPS. Gosh.)
    [super dealloc]; //Moved from beginning to end because of Abizern
}

#pragma mark -
#pragma mark Copying/Archiving

- (IRTileset*)copyWithZone:(NSZone*)zone
{
    IRTileset* copy = [IRTileset new];
    [copy setTileTemplates:tileTemplates]; //No longer insertTileTemplates: because of Peter Hosey
    [copy setUniqueID:uniqueID];

    return copy; //No longer [copy autorelease] because of Jared P
}

- (void)encodeWithCoder:(NSCoder*)encoder
{
    [encoder encodeObject:uniqueID forKey:@"uniqueID"];
    [encoder encodeObject:tileTemplates forKey:@"tileTemplates"];
}

- (IRTileset*)initWithCoder:(NSCoder*)decoder
{
    [self init];

    [self setUniqueID:[decoder decodeObjectForKey:@"uniqueID"]];
    [self setTileTemplates:[decoder decodeObjectForKey:@"tileTemplates"]]; //No longer insertTileTemplates: because of Peter Hosey

    return self;
}

#pragma mark -
#pragma mark Public Accessors

@synthesize uniqueID;
@synthesize tileTemplates;

- (NSUInteger)countOfTileTemplates
{
    return [tileTemplates count];
}

- (void)insertTileTemplates:(NSArray*)someTileTemplates atIndexes:(NSIndexSet*)indexes
{
    [tileTemplates insertObjects:someTileTemplates atIndexes:indexes];
}

- (void)removeTileTemplatesAtIndexes:(NSIndexSet*)indexes
{
    [tileTemplates removeObjectsAtIndexes:indexes];
}

//These are for later.
#pragma mark -
#pragma mark Private Accessors

#pragma mark -
#pragma mark Other

@end

(編集:これまでに提案された変更を行い、理由を知る必要がある場合に備えて、どの回答がそれらについて議論しているかをコメントしました。)

4

4 に答える 4

5

…については言及しないでくださいself = [super init]。</p>

それで、なぜあなたはそれをしないのですか?

同じことが にも当てはまります。最初のオブジェクトを初期化したとは想定せずに、initWithCoder:によって返されたオブジェクトを使用する必要があります。[self init]

- (void)dealloc
{
    [super dealloc];
    [tileTemplates release];
}

アビゼルンが彼のコメントで言ったように、[super dealloc]最後に来るべきです。それ以外の場合は、割り当て解除されたオブジェクトのインスタンス変数にアクセスしています。

- (IRTileTemplate*)copyWithZone:(NSZone*)zone

ここでの戻り値の型は である必要がありid、NSCopying プロトコルによって宣言された戻り値の型と一致します。

{
    IRTileset* copy = [IRTileset new];
    [copy insertTileTemplates:tileTemplates atIndexes:[NSIndexSet indexSetWithIndex:0]];
    [copy setUniqueID:uniqueID];

1 つのインデックスに 0 個以上のオブジェクトを挿入しています。範囲でインデックス セットを作成します: 位置 = 0、長さ =tileTemplates配列の数。さらに良いことに、プロパティ値全体に代入するだけです:

copy.tileTemplates = self.tileTemplates;

または、インスタンス変数に直接アクセスします。

copy->tileTemplates = [tileTemplates copy];

copy(プロパティアクセサーをバイパスするときは自分で行う必要があることに注意してくださいcopy。また、コピーに代わって配列を ing していることに注意してください。)

    return [copy autorelease];
}

copyWithZone:自動解放されたオブジェクトを返すべきではありません。メモリ管理規則によれば、copyまたはの呼び出し元がcopyWithZone:コピーを所有します。つまり、コピーを解放するのは呼び出し元の仕事であり、 ではありませんcopyWithZone:

@synthesize tileTemplates;
[et al]

単一オブジェクト配列アクセサーも実装したい場合があります。

- (void) insertObjectInTileTemplates:(IRTileTemplate *)template atIndex:(NSUInteger)idx;
- (void) removeObjectFromTileTemplatesAtIndex:(NSUInteger)idx;

もちろん、これはオプションです。

于 2010-02-28T00:19:36.497 に答える
2

プロパティを外部オブジェクトによる任意の操作に対して開いたままにしておくことを除いて、それはかなり良いように見えます。理想的には、データはモデル クラス自体によってのみ直接操作され、外部オブジェクトは専用のメソッドを介してのみアクセスできるようにする必要があります。

たとえば、一部の外部コードがこれを呼び出す場合:

myIRTileset.tileTemplates=someArray;

ブーム、すべてのデータが失われました。

両方のデータ プロパティを読み取り専用として定義する必要があります。次に、クラス実装内で保持を管理するクラス内部のアクセサーを記述します。このように、外部オブジェクトが tileTemplates を変更する唯一の方法は、- insertTileTemplates:atIndexes:およびremoveTileTemplatesAtIndexes:メソッドを呼び出すことです。

Edit01:

最初はめちゃくちゃだったと思うので、もう一度やり直してみましょう。次のパターンでデータ モデル クラスを設定する必要があります。

インターフェース

@interface PrivateTest : NSObject {
@private 
    //iVar is invisible outside the class, even its subclasses
    NSString *privateString; 
@public
    //iVar is visible and settable to every object. 
    NSString *publicString; 
}
@property(nonatomic, retain)  NSString *publicString; //property accessors are visible, settable and getable. 
//These methods control logical operations on the private iVar.
- (void) setPrivateToPublic;  
- (NSString *) returnPrivateString;
@end

したがって、使用中は次のようになります。

実装

#import "PrivateTest.h"

//private class extension category defines 
// the propert setters and getters 
// internal to the class
@interface PrivateTest ()
@property(nonatomic, retain)  NSString *privateString;
@end

@implementation PrivateTest
//normal synthesize directives
@synthesize privateString; 
@synthesize publicString;

// Methods that control access to private
- (void) setPrivateToPublic{
    //Here we do a contrived validation test 
    if (self.privateString != nil) {
        self.privateString=self.publicString;
    }
}

- (NSString *) returnPrivateString{
    return self.privateString;
}

@end

次のように使用します。

PrivateTest *pt=[[PrivateTest alloc] init];
    // If you try to set private directly as in the next line
    // the complier throws and error
//pt.privateString=@"Bob"; ==> "object cannot be set - either readonly property or no setter found"
pt.publicString=@"Steve";
[pt setPrivateToPublic];
NSLog(@"private=%@",[pt returnPrivateString]); //==> "Steve"

これで、クラスのデータ整合性が完全に保証されました。アプリ内のすべてのオブジェクトはpublicString文字列プロパティを設定および取得できますが、外部オブジェクトはprivate.

これは、アプリ内の任意のオブジェクトによるインスタンスへのアクセスを安全に許可できることを意味します。小さなオブジェクトやメソッドの不注意なコード行によってすべてが破棄されることを心配する必要はありません。

于 2010-02-28T00:06:20.237 に答える
2

//ただし、プロトコルは既に IRTileset.h にリストされていますが、ここにリストする必要がありますか?

いいえ、すべきではありません。実装ファイルで宣言されているクラス拡張は拡張であるため、クラスがどのプロトコルに従うように宣言されているかを気にする必要はありません。

インスタンス変数の名前にアンダースコアを付けることをお勧めします: _tileTemplates。(純粋主義者は、アンダースコアを前に付けるのではなく、後ろに付けるように言うでしょう。恐れている場合はそうしてください。)

newクラスのインスタンス化には使用しないでください。私が理解している限り、決してお勧めしません。

[NSMutableArray new];                     //  :(
[NSMutableArray arrayWithCapacity:20];    //  :)

独自の割り当て解除を行う前に [super dealloc] を呼び出さないでください。これにより、特定の状況でクラッシュが発生する可能性があります。

- (void)dealloc
{
    [tileTemplates release];
    [super dealloc];          // Do this last
}

どのような型uniqueIDがあるかはわかりませんが、 にもリリースされるべきではないdeallocでしょうか?

@synthesize ディレクティブをファイルの途中に配置することは決してありません ('@implementation' のすぐ下に配置します)。

また、このクラスの役割について明確な考えがないことは、countOfTileTemplates私には良くありません。このクラスがタイル テンプレートを保持するために何をするかが明確であれば、「カウント」だけで十分でしょうか?

于 2010-02-28T00:30:42.963 に答える
0

2 つのマイナーな問題: 1 つは init メソッドです (文体的には 2 つの異なるリターン ポイントを持つことに反対していますが、それは私だけです)。ただし、スーパーの init がそれ自体または nil とは異なるオブジェクトを返すことを止めるものは何もありません。クラスまたはまったく別のオブジェクトです。このため、self = [super init]実際にはあまり役に立たないとしても、一般的には良い考えです。2 番目は copyWithZone メソッドにあります。tileTemplates をコピーしません。これは意図的なものである可能性がありますが、一般的には悪い考えです (不変でない限り)。オブジェクトをコピーすることは、新しいオブジェクトを割り当てることと同じ効果があると想定されています。カウント 1 を保持するため、自動解放しないでください。また、ゾーンで何もしていないように見えるので、おそらく次のようなものに置き換える必要があります

- (IRTileTemplate*)copyWithZone:(NSZone*)zone {
    IRTileset* copy = [[IRTileset allocWithZone:zone] init];
    [copy insertTileTemplates:[tileTemplates copyWithZone:zone]
                    atIndexes:[NSIndexSet indexSetWithIndex:0]];
    [copy setUniqueID:uniqueID];
    return copy;
}

それが私が見つけたすべてです。コピーの保持カウント (これは後でバグにつながります) を除いて、ほとんどは私が好むものです。よくできました

于 2010-02-28T00:05:35.213 に答える