最近写代码的过程中发现一个很常见的问题,细思则恐,越看越不懂。哈哈。。其实还是copy
与strong
的问题。其实之前也整理过这个知识点。
下面我们就来温故而知新吧。
先来说说常见的copy
与strong
,还是举例说的更明白,下面把string、arrary、dictionary都来测试一遍
上代码NSString
:
@property (nonatomic ,copy) NSString *strCopy;
@property (nonatomic ,strong) NSString *strStrong;
@property (nonatomic ,copy) NSMutableString *mstrCopy;
@property (nonatomic ,strong) NSMutableString *mstrStrong;
NSLog(@"NSString系列");
NSString *str = @"aaa";
_strCopy = str;
_strStrong = str;
NSLog(@"str改变之前str = %@ 内存地址 = %p",str,str);
NSLog(@"str改变之前strCopy = %@ 内存地址 = %p",_strCopy,_strCopy);
NSLog(@"str改变之前strStrong = %@ 内存地址 = %p",_strStrong,_strStrong);
str = @"bbb";
NSLog(@"str改变之后str = %@ 内存地址 = %p",str,str);
NSLog(@"str改变之后strCopy = %@ 内存地址 = %p",_strCopy,_strCopy);
NSLog(@"str改变之后strStrong = %@ 内存地址 = %p",_strStrong,_strStrong);
NSLog(@"\n");
NSLog(@"NSMutableString系列");
NSMutableString *mstr = [[NSMutableString alloc] initWithString:@"ccc"];
_mstrCopy = mstr;
_mstrStrong = mstr;
NSLog(@"mstr改变之前mstr = %@ 内存地址 = %p",mstr,mstr);
NSLog(@"mstr改变之前mstrCopy = %@ 内存地址 = %p",_mstrCopy,_mstrCopy);
NSLog(@"mstr改变之前mstrStrong = %@ 内存地址 = %p",_mstrStrong,_mstrStrong);
[mstr appendString:@"---ddd"];
NSLog(@"mstr改变之后mstr = %@ 内存地址 = %p",mstr,mstr);
NSLog(@"mstr改变之后mstrCopy = %@ 内存地址 = %p",_mstrCopy,_mstrCopy);
NSLog(@"mstr改变之后mstrStrong = %@ 内存地址 = %p",_mstrStrong,_mstrStrong);
打印结果:
NSString系列
str改变之前str = aaa 内存地址 = 0x103d430c0
str改变之前strCopy = aaa 内存地址 = 0x103d430c0
str改变之前strStrong = aaa 内存地址 = 0x103d430c0
str改变之后str = bbb 内存地址 = 0x103d43140
str改变之后strCopy = aaa 内存地址 = 0x103d430c0
str改变之后strStrong = aaa 内存地址 = 0x103d430c0
NSMutableString系列
mstr改变之前mstr = ccc 内存地址 = 0x60000101c9c0
mstr改变之前mstrCopy = ccc 内存地址 = 0x60000101c9c0
mstr改变之前mstrStrong = ccc 内存地址 = 0x60000101c9c0
mstr改变之后mstr = ccc---ddd 内存地址 = 0x60000101c9c0
mstr改变之后mstrCopy = ccc---ddd 内存地址 = 0x60000101c9c0
mstr改变之后mstrStrong = ccc---ddd 内存地址 = 0x60000101c9c0
结果我们看到,当使用下划线_
访问成员变量,对于NSString
,当数据源的值发生改变,数据源内存地址会发生改变,成员变量不论使用copy还是strong修饰,数据源改变前后,成员变量的值不发生改变且内存地址也不会改变。对于NSMutableString
,当数据源的值发生改变,数据源内存地址不会发生改变,成员变量不论使用copy还是strong修饰,成员变量的值不发生改变且内存地址也不会改变。
下面稍稍改动上面的代码部分,仔细观察,来看下面的代码
self.strCopy = str;
self.strStrong = str;
self.mstrCopy = mstr;
self.mstrStrong = mstr;
这次再来看打印结果:
NSString系列
str改变之前str = aaa 内存地址 = 0x1011a30c0
str改变之前strCopy = aaa 内存地址 = 0x1011a30c0
str改变之前strStrong = aaa 内存地址 = 0x1011a30c0
str改变之后str = bbb 内存地址 = 0x1011a3140
str改变之后strCopy = aaa 内存地址 = 0x1011a30c0
str改变之后strStrong = aaa 内存地址 = 0x1011a30c0
NSMutableString系列
mstr改变之前mstr = ccc 内存地址 = 0x600003f75440
mstr改变之前mstrCopy = ccc 内存地址 = 0xf5a5fcc5e9dfaef4
mstr改变之前mstrStrong = ccc 内存地址 = 0x600003f75440
mstr改变之后mstr = ccc---ddd 内存地址 = 0x600003f75440
mstr改变之后mstrCopy = ccc 内存地址 = 0xf5a5fcc5e9dfaef4
mstr改变之后mstrStrong = ccc---ddd 内存地址 = 0x600003f75440
这下看到区别了,这里就又得扩展一个小知识点。
@property声明的属性默认会生成一个_类型( _x )的成员变量,
同时也会生成setter/getter方法。
通过self.x 访问属性的方法包含了set和get方法。而通过下划线是获取自己的实例变量,不包含set和get的方法。
self.x是对属性的访问;
_x 是对成员变量的访问。
self方法实际上是用了get和set方法间接调用,下划线方法是直接对变量操作。
于是乎继续上面的结论:
当使用下划线self
访问成员变量,对于NSString
,当数据源的值发生改变,数据源内存地址会发生改变,数据源改变前后,成员变量不论使用copy还是strong修饰,成员变量的值不发生改变且内存地址也不会改变。
对于NSMutableString
,成员变量使用copy修饰的时候,会生成一块新的内存地址来存放数据且当数据源发生改变,成员变量的内存地址不会改变,值也不会改变,属于深拷贝。当数据源的值发生改变,数据源内存地址不会发生改变,数据源改变前后,成员变量使用strong修饰,成员变量的值随着数据源的改变而改变,内存地址不会改变。
归根结底,不同修饰符,对应的setter方法不同
strong对应的setter方法,是将_property先release([_property release]),然后将参数retain([property retain]),最后_property = property.
copy对应的setter方法,是将_property先release([_property release]),然后将参数内容copy([property copy]),创建一块新的内存地址,最后_property = property.
copy: 建立一个索引计数为1的对象,然后释放旧对象
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
copy其实是建立了一个相同的对象,而retain不是:
比如之前NSString的地址为0×1111
copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象retainCount为1,旧有对象没有变化
retain到另外一个NSString之后,地址相同(指针拷贝),内容肯定相同,对象的retainCount值+1
结论:retain是指针拷贝,copy是内容拷贝。
下面来看NSMutableArray
@property (nonatomic ,copy) NSMutableArray *marrCopy;
@property (nonatomic ,strong) NSMutableArray *marrStrong;
NSLog(@"NSMutableArray系列");
NSMutableArray *marr = [[NSMutableArray alloc] initWithArray:@[@"aaa",@"bbb"]];
_marrCopy = marr;
_marrStrong = marr;
NSLog(@"marr改变之前marr = %@ 内存地址 = %p",marr,marr);
NSLog(@"marr改变之前marrCopy = %@ 内存地址 = %p",_marrCopy,_marrCopy);
NSLog(@"marr改变之前marrStrong = %@ 内存地址 = %p",_marrStrong,_marrStrong);
[marr addObject:@"ddd"];
NSLog(@"marr改变之后marr = %@ 内存地址 = %p",marr,marr);
NSLog(@"marr改变之后marrCopy = %@ 内存地址 = %p",_marrCopy,_marrCopy);
NSLog(@"marr改变之后marrStrong = %@ 内存地址 = %p",_marrStrong,_marrStrong);
NSLog(@"self.marrCopy class name %@",[_marrCopy class]);
[_marrCopy removeObjectAtIndex:0];
NSLog(@"marrCopy改变之后marrCopy = %@ 内存地址 = %p",_marrCopy,_marrCopy);
打印结果:
NSMutableArray系列
marr改变之前marr = (aaa,bbb) 内存地址 = 0x60000380c810
marr改变之前marrCopy = (aaa,bbb) 内存地址 = 0x60000380c810
marr改变之前marrStrong = (aaa,bbb) 内存地址 = 0x60000380c810
marr改变之后marr = (aaa,bbb,ddd) 内存地址 = 0x60000380c810
marr改变之后marrCopy = (aaa,bbb,ddd) 内存地址 = 0x60000380c810
marr改变之后marrStrong = (aaa,bbb,ddd) 内存地址 = 0x60000380c810
self.marrCopy class name __NSArrayI
marrCopy改变之后marrCopy = (bbb,ddd) 内存地址 = 0x600000618cc0
当使用下划线_
访问成员变量,对于NSMutableArray
,当数据源的值发生改变,数据源内存地址不会发生改变,成员变量不论使用copy还是strong修饰,成员变量的值不发生改变且内存地址也不会改变。
copy申明的marrCopy,初始化或赋值之后,打印其class
为NSArray
,对_marrCopy
执行增,删没有影响。
我们改成self
语法来看
self.marrCopy = marr;
self.marrStrong = marr;
//[self.marrCopy removeObjectAtIndex:0];
结果如下:
NSMutableArray系列
marr改变之前marr = (aaa,bbb) 内存地址 = 0x600000ebad00
marr改变之前marrCopy = (aaa,bbb) 内存地址 = 0x6000000cadc0
marr改变之前marrStrong = (aaa,bbb) 内存地址 = 0x600000ebad00
marr改变之后marr = (aaa,bbb,ddd) 内存地址 = 0x600000ebad00
marr改变之后marrCopy = (aaa,bbb) 内存地址 = 0x6000000cadc0
marr改变之后marrStrong = (aaa,bbb,ddd) 内存地址 = 0x600000ebad00
self.marrCopy class name __NSArrayI
执行remove会崩溃
当使用strong时, marr和marrStrong都指向同一块内存区域,当marr改变时, marrStrong的内容也会改变,两者是一样的
当使用copy时, marrCopy在赋值之前,将marr内容复制,创建一块新的内存空间,所以两者不同,不会随它改变
copy申明的marrCopy,初始化或赋值之后,打印其class
为NSArray
变成不可变数组,对self. marrCopy
执行增,删会跑出错误。这是因为copy属性修饰后,在初始化或赋值时,会先执行copy操作,然后赋值。
NSMutableDictionary
就不用再写了,效果与NSMutableArray
是一样的。
在实际开发中,我们需要对可变数组、字典使用strong属性修饰,并且使用self
调用
下面该说说我遇到的问题了,page1页面有一个table的NSMutableArray数据源,里面有N个model,model中的collection(收藏)字段为NSString类型。page1页面push到page2页面时,传递参数model。在page2页面修改model的collection字段,pop回page1页面后,数据源也对应着进行了改变。由此引发思考。
上demo
page1页面
@property (nonatomic ,strong) NSMutableArray *dataSource;
TestModel *model1 = [[TestModel alloc] init];
model1.collection = @"1";
TestModel *model2 = [[TestModel alloc] init];
model2.collection = @"1";
TestModel *model3 = [[TestModel alloc] init];
model3.collection = @"1";
_dataSource = [@[model1 ,model2 ,model3] mutableCopy];
NSLog(@"model改变之前,model的地址:%p",_dataSource[0]);
NSLog(@"model改变之前,model中collection的地址:%p",((TestModel *)(_dataSource[0])).collection);
NSLog(@"model改变之前,model中collection的值:%@",((TestModel *)(_dataSource[0])).collection);
- (void)nextPage {
SecondViewController *vc = [[SecondViewController alloc] init];
vc.testModel = _dataSource[0];
[self.navigationController pushViewController:vc animated:YES];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"返回页面page1后,model的地址:%p",_dataSource[0]);
NSLog(@"返回页面page1后,model中collection的地址:%p",((TestModel *)(_dataSource[0])).collection);
NSLog(@"返回页面page1后,model中collection的值:%@",((TestModel *)(_dataSource[0])).collection);
}
page2
@interface SecondViewController : UIViewController
@property (nonatomic ,strong)TestModel *testModel;
@end
-(void)setTestModel:(TestModel *)testModel {
_testModel = testModel;
NSLog(@"model传递到page2页面,model的地址:%p",_testModel);
NSLog(@"model传递到page2页面,model中collection的地址:%p",_testModel.collection);
NSLog(@"model传递到page2页面,model中collection的值:%@",_testModel.collection);
_testModel.collection = @"0";
NSLog(@"model传递到page2页面,model修改后,model的地址:%p",_testModel);
NSLog(@"model传递到page2页面,model修改后,model中collection的地址:%p",_testModel.collection);
NSLog(@"model传递到page2页面,model修改后,model中collection的值:%@",_testModel.collection);
}
开始查看结果:
model改变之前,model的地址:0x600003c606a0
model改变之前,model中collection的地址:0x10e67d148
model改变之前,model中collection的值:1
model传递到page2页面,model的地址:0x600003c606a0
model传递到page2页面,model中collection的地址:0x10e67d148
model传递到page2页面,model中collection的值:1
model修改后,model的地址:0x600003c606a0
model传递到page2页面,model修改后,model中collection的地址:0x10e67d0a8
model传递到page2页面,model修改后,model中collection的值:0
返回页面page1后,model的地址:0x600003c606a0
返回页面page1后,model中collection的地址:0x10e67d0a8
返回页面page1后,model中collection的值:0
返回页面后,我们看到数据源也跟着改变了。
如果说我们这样修改一下代码呢?
@interface SecondViewController : UIViewController
@property (nonatomic ,copy)TestModel *testModel;
@end
运行结果还是同上一样。
如果说我们不希望改变数据源的话,我们这么写,前提是先给model实现NSCopying协议
- (void)nextPage {
SecondViewController *vc = [[SecondViewController alloc] init];
vc.testModel = [_dataSource[0] copy];
[self.navigationController pushViewController:vc animated:YES];
}
打印结果
model改变之前,model的地址:0x600000a448f0
model改变之前,model中collection的地址:0x10312b148
model改变之前,model中collection的值:1
model传递到page2页面,model的地址:0x600000a48ad0
model传递到page2页面,model中collection的地址:0x10312b148
model传递到page2页面,model中collection的值:1
model传递到page2页面,model修改后,model的地址:0x600000a48ad0
model传递到page2页面,model修改后,model中collection的地址:0x10312b0a8
model传递到page2页面,model修改后,model中collection的值:0
返回页面page1后,model的地址:0x600000a448f0
返回页面page1后,model中collection的地址:0x10312b148
返回页面page1后,model中collection的值:1
这是又回到小白时代了吧。。。。欢迎评论你的心得!
网友评论