美文网首页
iOS数据传递过程中数据源的改变copy与strong

iOS数据传递过程中数据源的改变copy与strong

作者: th先生 | 来源:发表于2020-04-24 15:11 被阅读0次

最近写代码的过程中发现一个很常见的问题,细思则恐,越看越不懂。哈哈。。其实还是copystrong的问题。其实之前也整理过这个知识点。
下面我们就来温故而知新吧。

先来说说常见的copystrong,还是举例说的更明白,下面把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,初始化或赋值之后,打印其classNSArray,对_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,初始化或赋值之后,打印其classNSArray变成不可变数组,对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

这是又回到小白时代了吧。。。。欢迎评论你的心得!

相关文章

网友评论

      本文标题:iOS数据传递过程中数据源的改变copy与strong

      本文链接:https://www.haomeiwen.com/subject/ytmaihtx.html