集合对象可变与不可变的那点事

栏目: IOS · 发布时间: 6年前

内容简介:在文章今天跟大家分享一下集合类数据的可变与不可变性,再结合为了说明问题,这里,我选用数组(NSArray)作为集合对象的代表,其他的集合类以此类推即可。

在文章 NSString NSMutableString 可变与不可变的那些事儿 分享了关于 NSStringNSMutableStringcopy 以及 mutableCopy 之间的点滴。

今天跟大家分享一下集合类数据的可变与不可变性,再结合 copy 以及 mutableCopy 说一说注意事项。如果你仔细看过 NSString NSMutableString 可变与不可变的那些事儿 这篇文章,那么接下来看本篇会很轻松。

本篇内容主要涉及以下几个方面:

  • 在 OC 中的集合对象
  • 集合对象的 copy、mutableCopy
  • 可变与不可变集合对象之间等号赋值
  • property 中的集合对象的 copy 和 strong
  • 实际案例分析

为了说明问题,这里,我选用数组(NSArray)作为集合对象的代表,其他的集合类以此类推即可。

集合对象

Objective-C 中,非集合类对象指的是 NSStringNSNumberNSValue 之类的对象,除了 NSString 有对应的可变类 NSMutableString 外, NSNumberNSValue 都没有可变类与其对应。

集合类对象是指 NSArrayNSMutableArrayNSDictionaryNSMutableDictionaryNSSetNSMutableSet 之类的对象。

集合对象的 copy、mutableCopy

看一个具体例子,请接着看下面的示例代码和说明。

例子1:NSArray 的 copy、mutableCopy

NSArray *array = [NSArray arrayWithObjects:@"veryitman.com", nil];
NSLog(@"array addr: %p, array: %@ ", array, array);

// 地址未变和array一致,内容也一致
NSArray *array1 = array;
NSLog(@"array1 addr: %p, array1: %@", array1, array1);

// 地址未变和array一致,内容也一致
// copy 之后仍然是不可变的数组对象
id array2 = [array copy];
NSLog(@"array2 addr: %p, array2: %@", array2, array2);

// 地址改变
id array3 = [array mutableCopy];
NSLog(@"array3 addr: %p, array3: %@", array3, array3);

// 进一步说明了经过mutableCopy后,array3变成了可变数组
[(NSMutableArray *)(array3) addObject:@"my blog"];
NSLog(@"array3 addr: %p, array3: %@", array3, array3);

// 因为array3地址变了,不会影响array的地址和值
NSLog(@"array addr: %p, array: %@ ", array, array);

小结 1:

1、不可变数组 copy 之后,仍然是不可变数组,其地址和内容不变,即拷贝了原对象的内容和指针,属于指针拷贝。

2、不可变数组 mutableCopy 之后,变成了可变数组,其地址发生了变化,即只拷贝了原对象的内容,指针没有拷贝,属于内容拷贝。

3、不可变数组之间的 等号(=) 赋值,是指针拷贝。

例子2:NSMutableArray 的 copy、mutableCopy

NSMutableArray *marray = [NSMutableArray arrayWithObjects:@"veryitman.com", nil];
NSLog(@"marray addr: %p, marray: %@ ", marray, marray);

// 地址未变和marray一致,内容也一致
NSMutableArray *marray1 = marray;
NSLog(@"marray1 addr: %p, marray1: %@ ", marray1, marray1);

// copy 之后,地址改变且变成了不可变的数组对象
id marray2 = [marray copy];
NSLog(@"marray2 addr: %p, marray2: %@ ", marray2, marray2);

// mutableCopy 之后,地址改变但仍是可变数组对象
id marray3 = [marray mutableCopy];
NSLog(@"marray3 addr: %p, marray3: %@ ", marray3, marray3);

// Crash:进一步说明了可变数组对象经过 copy 之后变成了不可变的marray2
// -[__NSSingleObjectArrayI addObject:]: unrecognized selector sent to instance 0x600002cbd320
// *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
// reason: '-[__NSSingleObjectArrayI addObject:]: unrecognized selector sent to instance 0x600002cbd320'
// [(NSMutableArray *)(marray2) addObject:@"my blog"];
// NSLog(@"marray2 addr: %p, marray2: %@ ", marray2, marray2);

// 进一步证明了mutableCopy 之后,marray3是可变数组
[(NSMutableArray *)(marray3) addObject:@"my blog"];
NSLog(@"marray3 addr: %p, marray3: %@ ", marray3, marray3);

// 因为marray3地址改变了,所以对marray3的操作不会影响原来的数组对象marray
// marray 地址和内容保持不变
NSLog(@"marray addr: %p, marray: %@ ", marray, marray);

小结 2:

1、可变数组 copy 之后,会变成不可变数组,其内容不变,但是地址改变了,即只拷贝了原对象的内容,没有进行指针拷贝,属于内容拷贝。

2、可变数组 mutableCopy 之后,仍然是不可变数组,其地址发生了变化,内容没有变化,即只拷贝了原对象的内容,指针没有拷贝,属于内容拷贝。

3、可变数组之间 等号(=) 赋值,是指针拷贝。

例子3:NSMutableArray 和 NSArray 之间等号赋值

/** 向不可变数组赋值可变数组 */
{
    NSMutableArray *tDatas = [NSMutableArray arrayWithObjects:@"veryitman.com", nil];
    NSLog(@"--1--- tDatas addr: %p, tDatas: %@", tDatas, tDatas);
    
    // 类似但不同于可变数组的mutableCopy操作,此时 array 的地址未变和tDatas地址一致
    // array的内容和地址未发生变化,和tDatas一致
    NSArray *array = tDatas;
    NSLog(@"--2--- array addr: %p, array: %@", array, array);
}
    
 /** 向可变数组赋值不可变数组 */
{
    NSArray *tDatas = [NSArray arrayWithObjects:@"veryitman.com", nil];
    NSLog(@"--1--- tDatas addr: %p, tDatas: %@", tDatas, tDatas);
    
    // 类似进行了不可变数组的 copy 操作
    // array 仍旧是不可变的,地址和内容与tDatas一致
    NSMutableArray *array = tDatas;
    NSLog(@"--2--- array addr: %p, array: %@", array, array);
    
    // crash: 还是不可变的数组
    // -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x6000025499c0
    // Terminating app due to uncaught exception 'NSInvalidArgumentException',
    // reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x6000025499c0'
    // [array addObject:@"blog"];
}

输出结果,如下:

--1--- tDatas addr: 0x6000023a8bd0, tDatas: (
    "veryitman.com"
)
--2--- array addr: 0x6000023a8bd0, array: (
    "veryitman.com"
)

--1--- tDatas addr: 0x600002dbbf00, tDatas: (
    "veryitman.com"
)
--2--- array addr: 0x600002dbbf00, array: (
 	 "veryitman.com"
)

以上是使用 NSArrayNSMutableArray 来进行测试的, NSDictionaryNSSet 以及其对应的可变类型都遵循上面总结的内容。

copy、strong 修饰属性

在属性中,我们如何来选择 copy 或者 strong 来作为集合数据的修饰语呢?

根据上面示例分析结果可以看出,在属性中,如果使用 strong 修饰不可变数组,那么在使用过程中(被可变数组赋值)该不可变数组有可能会变为可变数组。如果使用 copy 修饰可变数组,那么在使用过程中(被不可变数组赋值)该可变数组有可能变为不可变数组。

小结 3:

当修饰可变类型的属性时,如 NSMutableArrayNSMutableDictionaryNSMutableSet 等集合类型时,用 strong 修饰。

当修饰不可变类型的属性时,如 NSArrayNSDictionary 、、 NSSet 等集合类型时,用 copy 修饰。

大家如果有兴趣可以参考文章 NSString NSMutableString 可变与不可变的那些事儿 的做法来验证上面的理论知识。

实际案例分析

再给大家举个实际的开发案例,我们需要定时上报目采集APP的数据,这个需求看起来是没有任何难度的。

我们使用代码来模拟一下上报数据的这个过程。

// 采集到的数据
NSMutableDictionary *tDatas = [NSMutableDictionary dictionaryWithCapacity:5];
[tDatas setObject:@"https://" forKey:@"req_m"];
NSLog(@"--采集数据--- tDatas addr: %p, tDatas: %@", tDatas, tDatas);
// 开始发送
[self sendDatas:tDatas];
NSLog(@"--上报完成,原数据--- tDatas addr: %p, tDatas: %@", tDatas, tDatas);

发送数据的模拟示例如下:

- (void)sendDatas:(NSDictionary *)datas
{
    NSLog(@"--上报中--- datas addr: %p, datas: %@", datas, datas);
    
    /** 下面两行代码只是为了模拟原数据被外界在传输过程中被改变,比如其他采集线程改变了它 */
    if ([datas isKindOfClass:[NSMutableDictionary class]]) {
        [(NSMutableDictionary *)datas setObject:@"veryitman.com" forKey:@"test_m"];
    }
    
    NSLog(@"--上报完成--- datas addr: %p, datas: %@", datas, datas);
}

根据上面 例子3 提到的,不可变向可变等号赋值时,原不可变对象会变成可变对象。

控制台输出日志,如下所示:

--采集数据--- tDatas addr: 0x600000b788e0, tDatas: {
    req_m = https://;
}

--上报中--- datas addr: 0x600000b788e0, datas: {
    req_m = https://;
}

--上报完成--- datas addr: 0x600000b788e0, datas: {
    req_m = https://;
    test_m = veryitman.com;
}

--上报完成,原数据--- tDatas addr: 0x600000b788e0, tDatas: {
    req_m = https://;
    test_m = veryitman.com;
}

下面代码的代码,我是为了模拟原数据被其他代码改变了的情况,只是为了说明,不可变对象容易被外界影响和改变。

/** 下面两行代码只是为了模拟原数据被外界在传输过程中被改变,比如其他采集线程改变了它 */
if ([datas isKindOfClass:[NSMutableDictionary class]]) {
    [(NSMutableDictionary *)datas setObject:@"veryitman.com" forKey:@"test_m"];
}

上面的总结又提到无论是可变对象还是不可变对象经过 copy 之后都是不可变对象的原理,我们修改一下代码,示例如下:

- (void)sendDatas:(NSDictionary *)datas
{
    NSDictionary *copy_datas = [datas copy];
    
    NSLog(@"--上报中--- copy_datas addr: %p, copy_datas: %@", copy_datas, copy_datas);
    
    if ([copy_datas isKindOfClass:[NSMutableDictionary class]]) {
        [(NSMutableDictionary *)copy_datas setObject:@"veryitman.com" forKey:@"test_m"];
    } else {
        NSLog(@"Yes, copy_datas 是不可变字典。");
    }
    
    NSLog(@"--上报完成--- copy_datas addr: %p, copy_datas: %@", copy_datas, copy_datas);
}

输出结果如下:

 --采集数据--- tDatas addr: 0x600003b08740, tDatas: {
    req_m = https://;
}

--上报中--- copy_datas addr: 0x600003b08700, copy_datas: {
    req_m = https://;
}

Yes, copy_datas 是不可变字典。

--上报完成--- copy_datas addr: 0x600003b08700, copy_datas: {
    req_m = https://;
}

--上报完成,原数据--- tDatas addr: 0x600003b08740, tDatas: {
    req_m = https://;
}

扫码关注,期待与你的交流~

集合对象可变与不可变的那点事


以上所述就是小编给大家介绍的《集合对象可变与不可变的那点事》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web 2.0界面设计模式

Web 2.0界面设计模式

黄玮 / 电子工业出版社 / 2013-9-1 / 59

本书集Web 2.0的发展及特点、Web 2.0界面设计模式基本理论、实际模式实践及代码实现等诸多内容于一身,具有很强的实用性。这些内容不是简单的顺序堆砌,而是以Web 2.0界面设计模式和应用为主线,其中完美地穿插了各种与之相关的Web 2.0设计理念、用户行为模式、用户体验及基于Dojo的实现方式等相关知识,真正做到将Web 2.0界面设计模式所需要的方方面面的知识有机地融为一个整体。实现不需......一起来看看 《Web 2.0界面设计模式》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具