Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding @property setters with ARC for @property with 'copy'

@interface Unicorn
@property (nonatomic, copy) NSString *name;
@end

Is it like this?

- (void)setName:(NSString *)name
{
   _name = name;
}

Or is it like this?

- (void)setName:(NSString *)name
{
   _name = [name copy]; 
}
like image 953
hfossli Avatar asked Dec 15 '22 01:12

hfossli


2 Answers

You should do the second.

If you override the setter you are taking control of the semantics of copy vs non copy. ARC will do the correct thing with regards to inserting retain/releases with the assignment but it won't call copy for you

My source? Test it

@interface UnicornWithCopyCall : NSObject

@property (nonatomic, copy) NSString *name;

@end

@implementation UnicornWithCopyCall

- (void)setName:(NSString *)name
{
  _name = [name copy];
}

@end

@interface UnicornWithOutCopyCall : NSObject

@property (nonatomic, copy) NSString *name;

@end

@implementation UnicornWithOutCopyCall

- (void)setName:(NSString *)name
{
  _name = name;
}

@end

Then exercise this with

UnicornWithCopyCall *unicorn = [[UnicornWithCopyCall alloc] init];
unicorn.name = name;

NSLog(@"%p %p", name, unicorn.name);

UnicornWithOutCopyCall *unicornWithOutCopyCall = [[UnicornWithOutCopyCall alloc] init];
unicornWithOutCopyCall.name = name;

NSLog(@"%p %p", name, unicornWithOutCopyCall.name);

Without the copy call the pointers are identical, whereas with the copy call you get a new object, which is a copy.

like image 196
Paul.s Avatar answered May 03 '23 23:05

Paul.s


It should be the second one. And the second part of this is entirely optional, but it can sometimes be helpful to only set the string if the new value is different from the old. This example is by value, meaning that it doesn't matter if _name and name are references to the same object and the check will work when ever the two objects contain the same value, but you could just as easily to a reference comparison.

- (void)setName:(NSString *)name
{
    if (![_name isEqualToString:name]) {
        _name = [name copy];
    }
}

And here's another example from CocoaWithLove

- (void)setStringValue:(NSString *)aString
{
    if (stringValue == aString)
    {
        return;
    }
    NSString *oldValue = stringValue;
    stringValue = [aString copy];
    [oldValue release];
}
like image 29
Mick MacCallum Avatar answered May 04 '23 00:05

Mick MacCallum