Objective C - Assign, Copy, Retain

Objective C - Assign, Copy, Retain

Updated Answer for Changed Documentation

The information is now spread across several guides in the documentation. Here's a list of required reading:

  • Cocoa Core Competencies: Declared property
  • Programming with Objective-C: Encapsulating Data
  • Transitioning to ARC Release Notes
  • Advanced Memory Management Programming Guide
  • Objective-C Runtime Programming Guide: Declared Properties

The answer to this question now depends entirely on whether you're using an ARC-managed application (the modern default for new projects) or forcing manual memory management.

Assign vs. Weak - Use assign to set a property's pointer to the address of the object without retaining it or otherwise curating it; use weak to have the property point to nil automatically if the object assigned to it is deallocated. In most cases you'll want to use weak so you're not trying to access a deallocated object (illegal access of a memory address - "EXC_BAD_ACCESS") if you don't perform proper cleanup.

Retain vs. Copy - Declared properties use retain by default (so you can simply omit it altogether) and will manage the object's reference count automatically whether another object is assigned to the property or it's set to nil; Use copy to automatically send the newly-assigned object a -copy message (which will create a copy of the passed object and assign that copy to the property instead - useful (even required) in some situations where the assigned object might be modified after being set as a property of some other object (which would mean that modification/mutation would apply to the property as well).

@property retain, assign, copy, nonatomic in Objective-C

The article linked to by MrMage is no longer working. So, here is what I've learned in my (very) short time coding in Objective-C:

nonatomic vs. atomic
- "atomic" is the default. Always use "nonatomic". I don't know why, but the book I read said there is "rarely a reason" to use "atomic". (BTW: The book I read is the BNR "iOS Programming" book.)

readwrite vs. readonly
- "readwrite" is the default. When you @synthesize, both a getter and a setter will be created for you. If you use "readonly", no setter will be created. Use it for a value you don't want to ever change after the instantiation of the object.

retain vs. copy vs. assign

  • "assign" is the default. In the setter that is created by @synthesize, the value will simply be assigned to the attribute. My understanding is that "assign" should be used for non-pointer attributes.
  • "retain" is needed when the attribute is a pointer to an object. The setter generated by @synthesize will retain (aka add a retain count) the object. You will need to release the object when you are finished with it.
  • "copy" is needed when the object is mutable. Use this if you need the value of the object as it is at this moment, and you don't want that value to reflect any changes made by other owners of the object. You will need to release the object when you are finished with it because you are retaining the copy.

Clarification on assign, retain, copy, strong?

To reiterate, it does depend on context. In an non-ARC situation:

@property (nonatomic, copy) NSMutableArray *myArray
@property (nonatomic, copy) NSString *myString
@property (nonatomic, retain) UIColor *myColor
//Note the change to an int rather than a pointer to an int
@property (nonatomic, assign) int myInt
//Note the change to an int rather than a pointer to an int
@property (nonatomic, assign) BOOL myBOOL

The copy on myArray is to prevent modification by another "owner" of the object you set. In an ARC project, things change a bit:

@property (nonatomic, copy) NSMutableArray *myArray
@property (nonatomic, copy) NSString *myString
@property (nonatomic, strong) UIColor *myColor
//Note the change to an int rather than a pointer to an int
@property (nonatomic, assign) int myInt
//Note the change to an int rather than a pointer to an int
@property (nonatomic, assign) BOOL myBOOL

The change is primarily to myColor in your situation. You wouldn't use retain as you aren't managing reference counting directly. The strong keyword is a way of asserting "ownership" of the property and similar to retain. An additional keyword is also provided, weak, that would typically be used in place of assign for object types. Apple's common example of a weak property is for delegates. I'd recommend going through the Transitioning to ARC Release Notes in addition to the Memory Management Guide a time or two as there is more nuance than can easily be covered in an SO post.

When to use assign or retain in properties declaration?

Assign is usually used for primitive types, the compiler will create the setter such that all that is done is a simple assign operation.

Whereas setting a value on a property with the 'retain' (now called "strong" with ARC) qualifier causes your backing instance variable to take ownership of (in other words retain) the object that was set.

With objects, if you don't want to take ownership as described and you're using ARC, you would most likely want to use the 'weak' qualifier instead of 'assign'.

objective-c retain , assign and copy?

alloc gives retain count 1.
self.objA = will give retain count 2 (because of the retain property)

Properties in Objective c. copy and retain

A property is just a declaration that allows for setters, getters, and dot-syntax accessors (interface variable hiding).

It does absolutely nothing on its own but allow you to use -[myInstance myProperty] to get the variable or use -[myInstance setMyProperty:] to set it (yes, the method name is auto-assigned to -setProperty: and -property).

When declaring a property, you have three categories - thread locking, access control, and memory management. You can only pick one of the modifiers for each category and if you do not pick one, it's auto-assigned to one automatically.

@property (, , ) id property;

The first category can either be atomic or nonatomic. The atomic modifier forces an @synchronized(myInstance) lock on the variable, to allow thread safety. The nonatomic does not use a synchronized-block, and is NOT thread safe. If you do not use either, it is automatically set to atomic.

The second category can either be readonly or readwrite. The readwrite modifier allows the property to be modified as well, and allows auto-generation of the -setProperty: method. When the readonly modifier is used, you cannot use the -setProperty: method. You must use the internal variable from within the object to set the variable directly.

The third category can either be assign, retain, and copy. The assign modifier means the internal object pointer is set to the pointer passed to the -setProperty: message. The retain modifier assigns the passed pointer and passes a -retain to the object.

The copy modifier does a straight-up clone of the object- a new pointer to a new object at a new address in the memory. This sets the internal object pointer to the copy of the passed object, by calling -copy on the passed object. The default modifier is assign, and the compiler will warn you if you do not set the memory management category modifier on an object - because an assign modifier on an object is frowned upon (unless explicitly declared).

For an example on -copy, look at this:

- (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);
}

There is an optional method name declaration modifier and is used like so: getter = myCustomPropertyGetter and setter = myCustomPropertySetter: (The colon : at the end of the setter method name is required because it denotes that an argument must be passed).

The second half of this is the property synthesizer or dynamizer. Once a property is declared (for example, myView) like so:

@property (nonatomic, retain) NSView *myView;

You would either: define the setter and getter yourself; @synthesize the setter and getter; @dynamic the property to say that it exists in a category or the main class, or may be added at runtime (not a fun idea, mind you, and could cause bad runtime exceptions).

The first example, writing the methods yourself would look like this:

// 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

The second example would be to auto-synthesize it using the @synthesize directive:

@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

Under the last example, perhaps the most confusing, because it requires the use of the @dynamic directive, you require something of a category or a runtime method addition:

@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

The @property declaration requires one of the three above declarations to be present- it does not do anything on its own. What it DOES allow, however is dot-syntax accessors (Java-like accessors for setting and getting properties).

For example, @property (copy) NSString *myName; could be accessed using -[myObject myName] and set using -[myObject setMyName:].

Now it can be set using myObjectInstance.myName = @"Bob"; and gotten using myObjectInstance.myName. Utilizing all the above concepts, one could create an object such as this one:

// 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

Note: This code was in no way tested.
Now that you have a GXBufferQueue, all the following works:

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);

As you can see, there's a lot possible with properties and a lot more that can be done with them than just variable declaration. If there are any questions or anything the community would like me to add/rectify to/in the post, please leave a comment! :D



Related Topics



Leave a reply



Submit