David LIu David LIu - 2 months ago 12
iOS Question

iOS retain copy issue

code1 use "_" to do the assignment:

@interface ViewController ()

@property (nonatomic,retain) NSMutableString *rrstr;
@property (nonatomic,copy) NSMutableString *copystr;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSMutableString *ssss = [[NSMutableString alloc]initWithString:@"ddddd"];
_rrstr = ssss;
_copystr = ssss;
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);

[ssss appendString:@"1231"];
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);

[ssss deleteCharactersInRange:NSMakeRange(1, 3)];
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);
}


code1 output:

2016-09-19 10:44:29.190 retin[1160:72426] ddddd===ddddd===ddddd
2016-09-19 10:44:29.191 retin[1160:72426] ddddd1231===ddddd1231===ddddd1231
2016-09-19 10:44:29.192 retin[1160:72426] dd1231===dd1231===dd1231


code2 use the "." to do the assignment:

@interface ViewController ()

@property (nonatomic,retain) NSMutableString *rrstr;
@property (nonatomic,copy) NSMutableString *copystr;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSMutableString *ssss = [[NSMutableString alloc]initWithString:@"ddddd"];
self.rrstr = ssss;
self.copystr = ssss;
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);

[ssss appendString:@"1231"];
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);

[ssss deleteCharactersInRange:NSMakeRange(1, 3)];
NSLog(@"%@===%@===%@",ssss,self.rrstr,self.copystr);


}


code2 output:

2016-09-19 10:50:09.079 retin[1190:75922] ddddd===ddddd===ddddd
2016-09-19 10:50:09.079 retin[1190:75922] ddddd1231===ddddd1231===ddddd
2016-09-19 10:50:09.080 retin[1190:75922] dd1231===dd1231===ddddd


I was confused why these two codes have different output? Shouldn't these codes have the same outputs? What the differences exactly is between the "_" and "." syntax?

Answer

When you declare an @property in Objective-C, the compiler does a few things for you.

  • It creates a backing instance variable to hold the property's value. By default this is _propertyName but you can override it with the @synthesize directive
  • It creates getter and setter functions. By default these simply set and get the backing instance variable, but again you can override these functions and provide your own setter and getter implementations.

Many people, incorrectly, think that _propertyName is just a "short cut" to access the property, but this is not the case. It accesses the backing variable directly, bypassing the setter and getter functions.

In many cases it doesn't matter, but in some cases it does, and one of those is the case you have discovered, a copy property.

copy is an attribute of the property, not the underlying variable, so when you declare

@property (nonatomic,copy)   NSMutableString *copystr;

Objective-C creates the following getter and setter functions that look something like:

-(NSMutableString *) copyStr {
    return _copyStr;
}

-(void) setCopyStr: (NSMutableString *)value {
    _copyStr = [value copy];
}

So, now you can see the difference between saying

_copyStr = ssss;   // This is a straight pointer assignment

and

 self.copyStr = ssss;  // This is actually a call to [self setCopyStr:ssss]

In the first case, the setter isn't called and therefore the copy is never taken; _copyStr is a reference to the ssss string, so when you change ssss that change is reflected in _copyStr since they are referencing the same object.

In the second case, the setter is called and ssss is copied and a reference to the new copy is assigned to _copyStr; Now, when ssss is changed, the copy referred to by _copyStr is unaffected.

In short, you should always use self.propertyName unless you have a specific reason for bypassing the setter/getter.

Comments