5

私がアップルのドキュメントretainから読んだことは、保持数を1増やし、リリースを1減らします。これは私には非常に明白です。

しかし、コピーして保持する場合、私は少し混乱しています。

私が試しているコードで説明させてください。

財産 - -

   @property(nonatomic, retain) NSMutableString *a;

   @property(nonatomic, copy) NSMutableString *b;


   @synthesize a = _a ,b = _b

   a=[[NSMutableString alloc]initWithString:@"Hello Ankit"];
   NSLog(@"a memory location A - %p", &a );
   b=[[NSMutableString alloc]initWithString:@"Hello Nigam"];
   NSLog(@"a memory location B- %p", &b );
   c= [[NSMutableString alloc]initWithString:@"Ankit Nigam"];
   NSLog(@"a memory location C %p",&c);


NSMutableString *temp =[[NSMutableString alloc]initWithString:@"hey"];

NSLog(@"temp = %@ %p",temp,&temp);
self.b = temp;
NSLog(@"B is now %@ %p",self.b,&b);

self.a = temp;
NSLog(@"A is now %@ %p",self.a,&a);

And i get the output as -- - -

2012-05-10 03:24:34.756 retainCountTest[2655:f803] a memory location A - 0x6d314fc
2012-05-10 03:24:34.757 retainCountTest[2655:f803] a memory location B- 0x6d31500
2012-05-10 03:24:34.764 retainCountTest[2655:f803] a memory location C 0x6d31504
2012-05-10 03:24:34.764 retainCountTest[2655:f803] temp = hey 0xbfffdd04
2012-05-10 03:24:34.764 retainCountTest[2655:f803] B is now hey 0x6d31500
 2012-05-10 03:24:34.765 retainCountTest[2655:f803] A is now hey 0x6d314fc

しかし、私がドキュメントから理解しているように、保持オブジェクトは同じメモリアドレスを持っている必要がありますが、コピーオブジェクトは異なるメモリ位置で新しいオブジェクトを作成します。

再びログをに変更すると---

self.b = temp;
NSLog(@"B is now %@ %p",self.b,&_b);

self.a = temp;
NSLog(@"A is now %@ %p",self.a,&_a);
It return me a complete different memory location for both the object.

2012-05-10 03:28:49.905 retainCountTest[2688:f803] a memory location A - 0x6d4a4ac
2012-05-10 03:28:49.906 retainCountTest[2688:f803] a memory location B- 0x6d4a4b0
2012-05-10 03:28:49.907 retainCountTest[2688:f803] a memory location C 0x6d4a4b4
2012-05-10 03:28:49.907 retainCountTest[2688:f803] temp = hey 0xbfffdd04
2012-05-10 03:28:49.908 retainCountTest[2688:f803] B is now hey 0x6d4a4c0
2012-05-10 03:28:49.908 retainCountTest[2688:f803] a is now hey 0x6d4a4bc

これらの保持とコピーの完全な概念を理解するのに役立つものはありますか?また、なぜ私はこれらの予期しない結果を得ているのですか。

どうもありがとう。

4

2 に答える 2

19

プロパティは、セッター、ゲッター、およびドット構文アクセサー(インターフェイス変数の非表示)を許可する単なる宣言です。

それ自体はまったく何もしませんが-[myInstance myProperty]、変数の取得または-[myInstance setMyProperty:]設定に使用できます(はい、メソッド名はとに自動割り当てされ-setProperty:ます-property)。

プロパティを宣言する場合、スレッドロック、アクセス制御、メモリ管理の3つのカテゴリがあります。各カテゴリの修飾子は1つしか選択できません。選択しない場合は、自動的に1つに割り当てられます。

@property (<thread locking>, <access control>, <memory management>) id property;

最初のカテゴリはまたはのいずれatomicnonatomicです。修飾子はatomic、スレッドセーフを可能にするために、変数に@synchronized(myInstance)ロックを強制します。はnonatomic同期ブロックを使用せず、スレッドセーフではありません。どちらも使用しない場合は、自動的にに設定されatomicます。

2番目のカテゴリは、またはのいずれreadonlyreadwriteです。修飾子を使用readwriteすると、プロパティも変更でき、-setProperty:メソッドの自動生成が可能になります。修飾子を使用する場合readonly、このメソッドは使用できません-setProperty:。変数を直接設定するには、オブジェクト内から内部変数を使用する必要があります。

3番目のカテゴリは、、、およびのいずれassignretainですcopyassign修飾子は、内部オブジェクトポインタがメッセージに渡されたポインタに設定されることを意味します-setProperty:。モディファイアはretain渡されたポインタを割り当て、-retainオブジェクトにを渡します。

copyモディファイアは、オブジェクトのストレートアップクローンを作成します。これは、メモリ内の新しいアドレスにある新しいオブジェクトへの新しいポインタです。これにより、渡されたオブジェクトを呼び出すことにより、渡されたオブジェクトのコピーへの内部オブジェクトポインタが設定され-copyます。デフォルトの修飾子はですassign。オブジェクトにメモリ管理カテゴリ修飾子を設定しない場合、コンパイラは警告を表示します。これは、オブジェクトのassign修飾子が(明示的に宣言されていない限り)無視されるためです。

-copyの例については、次を参照してください。

- (void)setProperty:(GXMyObject *)property {

    // This points to the original passed object.
    GXMyObject *original = property;

    // This points to a copy of the passed object.
    CGMyObject *copied = [property copy];

    // This points to yet another copy of the passed object-
    // Independent of the other copies and original.
    _property = [property copy];

    // The anotherProperty is now different on this copy
    // than on the original and the other copies.
    _property.anotherProperty = 4;

    // This will prove that they are all individual objects.
    NSLog(@"%p, %p, %p", original, copied, _property);
}

オプションのメソッド名宣言修飾子があり、次のように使用されます:getter = myCustomPropertyGetterおよび(引数を渡す必要があることを示すため、setterメソッド名の最後setter = myCustomPropertySetter:のコロンが必要です)。:

この後半は、プロパティシンセサイザーまたはダイナマイザーです。プロパティが宣言されると(たとえばmyView)、次のようになります。

@property (nonatomic, retain) NSView *myView;

セッターとゲッターを自分で定義します。@synthesizeセッターとゲッター。@dynamicカテゴリまたはメインクラスに存在する、または実行時に追加される可能性があることを示すプロパティ(楽しいアイデアではありません。気を付けてください。実行時の例外が発生する可能性があります)。

最初の例では、メソッドを自分で作成すると、次のようになります。

// In Apple's LLVM 3.1 Compiler, instance variables can be added 
// within {} below the @implementation as well as the @interface, 
// and in private categories (@interface GXMyClass ()) like before.
@implementation GXMyClass {
    // The internal object pointer is prefixed with an _ to avoid name confusions.
    NSView *_myView;
}

- (NSView *)myView {
    return _myView;
}

- (void)setMyView:(NSView *)myView {
    _myView = [myView retain];
}

@end

@synthesize2番目の例は、ディレクティブを使用して自動合成することです。

@implementation GXMyClass

// In the new Apple LLVM 3.1 Clang compiler, the = operator when used 
// next to the @synthesize directive declares an internal private 
// variable and automatically sets to that variable.
@synthesize myView = _myView;

// The internal variable name is now myOtherView, because we did not use the
// = operator to assign an internal variable name to the property.
@synthesize myOtherView;

@end

最後の例では、おそらく最も混乱します。@ dynamicディレクティブを使用する必要があるため、カテゴリまたはランタイムメソッドの追加が必要です。

@interface GXMyClass (InternalMethods)
@end

@implementation GXMyClass

// The = assignment operator does not work here.
@dynamic myView;

@end

@implementation GXMyClass (InternalMethods)

- (NSView *)myView {
    return [self methodThatReturnsAnNSView];
}

- (void)setMyView:(NSView *)myView {
    [self methodThatAcceptsAnNSViewArgument:myView];
}

@end

宣言では、上記の3つの@property宣言のいずれかが存在する必要があります。それ自体は何もしません。ただし、許可されるのはドット構文アクセサー(プロパティを設定および取得するためのJavaのようなアクセサー)です。

たとえば、を使用してアクセスし、を使用して設定@property (copy) NSString *myName;できます。-[myObject myName]-[myObject setMyName:]

myObjectInstance.myName = @"Bob";これで、を使用して設定し、を使用して取得できますmyObjectInstance.myName。上記のすべての概念を利用して、次のようなオブジェクトを作成できます。

// The GXBufferQueue is a queue which buffers all requests, till they are read
// asynchronously later. The backing store is an NSMutableArray to which all
// buffer writes are appended to, and from which the first object is pulled and
// returned when the buffer is read to.   
@interface GXBufferQueue

@property (nonatomic, readwrite, copy, setter = write:, getter = read) id buffer;

+ (GXBufferQueue *)queue;

@end

@implementation GXBufferQueue {
    // This queue is an internal array and is 'tacked on' to the @implementation
    // so no others can see it, and it can be marked @private so subclasses cannot
    // use it. It is also good code practice to have @interfaces composed of only
    // @properties, setters, and getters, rather than expose internal variables.
    NSMutableArray *_internalQueue;
}

+ (GXBufferQueue *)queue {
    return [[[GXBufferQueue alloc] init] autorelease];
}

- (id)init {
    if((self = [super init])) {
        _internalQueue = [[NSMutableArray alloc] init];
    }
}

- (void)write:(id)buffer {
    [_internalQueue addObject:buffer];
}

- (id)read {
    if(!(_internalQueue.count > 0)) return nil;

    id buffer = [_internalQueue objectAtIndex:0];
    [_internalQueue removeObjectAtIndex:0];
    return buffer;
}

@end

注:このコードはテストされていません。GXBufferQueueができたので、次のすべてが機能します。

GXBufferQueue *queue = [GXBufferQueue queue];

// Option One: using the traditional message syntax:
[queue write:@"This will be now added to the buffer."];
NSLog(@"Now the string written to the queue will be read \
and removed from the queue, like a stack pop. ", [queue read]);

// Option Two: using the new dot-syntax accessors:
queue.buffer = @"As clunky as this looks, it works the same as above.";
NSLog(@"These lines work just the same as the ones above: ", queue.buffer);

ご覧のとおり、プロパティには多くの可能性があり、変数の宣言だけでなく、プロパティを使用して実行できることもたくさんあります。質問やコミュニティが投稿に追加/修正したいことがある場合は、コメントを残してください!:D

于 2012-05-09T23:27:19.213 に答える
4

はい、retainCount役に立たない-それを呼び出さないでください-そしてそれcopyが常に新しいインスタンスを返すと仮定することは正しくありません。

不変オブジェクトが送信される場合、copy通常は次のように実装されます。

- copy { return [self retain]; }

つまり、内容は変更できないため、実際にコピーする必要はありません。

もちろん、バイナリの文字列にコンパイルされた静的な文字列をいじくり回しているので、実装はおそらく次のようになります。

- retain { return self; }
- copy { return self; }
- (void)release { ; }
- (void)dealloc { ; }

たぶん-上記のすべては、いつでも変更される可能性のある実装の詳細です。ただし、肝心なのは、上記のすべてが保持/解放/コピーなどの契約を満たしているということです...

于 2012-05-09T22:35:17.310 に答える