好东西小知识点iOS Developer

NSString, NSArray, NSDictionary

2017-02-28  本文已影响454人  果哥爸

NSStringNSArray 在@property的时候,到底是用strong还是用copy修饰,这里依据自己的理解,做一个总结。

一. NSString

声明变量:

// strong  类型  NSString
@property (nonatomic, strong) NSString *strongString;
// copy    类型  NSString
@property (nonatomic, copy)   NSString *copyedString;

这里分别声明了一个用strong修饰的strongString变量和一个用copy修饰的copyedString变量。(注意: 不可以写成copyStr会报property follows cocoa naming convention for returning 'owned' objects意思是不能使用copy来作为开头命名,copy是cocoa用的)

NSString 赋值

NSString *tmpString = @"123456";

self.strongString = tmpString;
self.copyedString = tmpString;

NSLog(@"tmpString: %@, %p", tmpString, tmpString);
NSLog(@"self.strongString: %@, %p", self.strongString, self.strongString);
NSLog(@"self.copyedString: %@, %p", self.copyedString, self.copyedString);

查看输出结果

tmpString: 123456, 0x106074078
self.strongString: 123456, 0x106074078
self.copyedString: 123456, 0x106074078

根据输出的内存地址和内容,我们可以发现不管是strong修饰还是copy修饰,指向的都是同一个内存地址,也就是tmpString的地址。strongString和copyedString都只是对tmpString的引用,只会导致tmpString的计算器加1,并没有拷贝一份新的,tmpString的retainCount应该是3.

NSMutableString 赋值

NSMutableString *tmpMutableString = [[NSMutableString alloc] initWithString:@"123456"];

self.strongString = tmpMutableString;
self.copyedString = tmpMutableString;

NSLog(@"tmpMutableString: %@, %p", tmpMutableString, tmpMutableString);
NSLog(@"self.strongString: %@, %p", self.strongString, self.strongString);
NSLog(@"self.copyedString: %@, %p", self.copyedString, self.copyedString);

[tmpMutableString appendString:@"789"];

NSLog(@"tmpMutableString: %@, %p", tmpMutableString, tmpMutableString);
NSLog(@"self.strongString: %@, %p", self.strongString, self.strongString);
NSLog(@"self.copyedString: %@, %p", self.copyedString, self.copyedString);

输出打印结果

tmpMutableString: 123456, 0x600000260640
self.strongString: 123456, 0x600000260640
self.copyedString: 123456, 0xa003635343332316

tmpMutableString: 123456789, 0x600000260640
self.strongString: 123456789, 0x600000260640
self.copyedString: 123456, 0xa003635343332316

这时候我们可以看到,copy修饰的copyedString 字符串, 已经不再是简单的引用了,而是拷贝一个新的,让copyedStr指向了一个新的地址。此时tmpMutableString的ratainCount应该是2.

然后我们将[tmpMutableString appendString:@"789"];加上“789”,输出后,发现tmpMutableString和strongString会随之改变,但copyedString则不会随之变化。

分析

  1. 当源字符串是NSString类型时,由于是不可变字符串,所以,不管是使用strong还是copy修饰的字符串,都是指向源字符串,copy操作只是做了次浅拷贝。

  2. 当源字符串是NSMutableString时,strong修饰的字符串只是将源字符串的引用计算器加1,依旧指向源字符串,而copy修饰的字符串则是对源字符串做了次深拷贝,从而生成一个新的对象,self.copyedString的指针指向了这个新的对象。并且这个copy属性修饰的对象始终是NSString类型而不是NSMutableString类型,如果想让拷贝过来的对象可变,就需要使用mutableCopy。

总结

相遇.jpeg

二. NSArray

声明变量

// strong  类型 NSArray
@property (nonatomic, strong) NSArray  *strongArray;
// copy    类型 NSArray
@property (nonatomic, copy)   NSArray  *copyedArray;

NSArray 赋值

NSArray *tmpArray = [NSArray arrayWithObjects:@"1",@"2", nil];

self.strongArray = tmpArray;
self.copyedArray = tmpArray;

NSLog(@"tmpArray: %@, %p", tmpArray, tmpArray);
NSLog(@"self.strongArray: %@, %p", self.strongArray, self.strongArray);
NSLog(@"self.copyedArray: %@, %p", self.copyedArray, self.copyedArray);

输出结果

tmpArray: (
    1,
    2
), 0x610000037220
self.strongArray: (
    1,
    2
), 0x610000037220
 self.copyedArray: (
    1,
    2
), 0x610000037220

** NSMutableArray 赋值**

NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects:@"1",@"2", nil];

self.strongArray = tmpArray;
self.copyedArray = tmpArray;

NSLog(@"tmpArray: %@, %p", tmpArray, tmpArray);
NSLog(@"self.strongArray: %@, %p", self.strongArray, self.strongArray);
NSLog(@"self.copyedArray: %@, %p", self.copyedArray, self.copyedArray);

[tmpArray addObject:@"3"];
NSLog(@"tmpArray: %@, %p", tmpArray, tmpArray);
NSLog(@"self.strongArray: %@, %p", self.strongArray, self.strongArray);
NSLog(@"self.copyedArray: %@, %p", self.copyedArray, self.copyedArray);

** 输出结果 **

 tmpArray: (
    1,
    2
), 0x600000052930
self.strongArray: (
    1,
    2
), 0x600000052930
self.copyedArray: (
    1,
    2
), 0x600000025c60



tmpArray: (
    1,
    2,
    3
), 0x600000052930
 self.strongArray: (
    1,
    2,
    3
), 0x600000052930
self.copyedArray: (
    1,
    2
), 0x600000025c60

从以上测试输出结果,我们可以很明显的看出,NSArray用strong或者copy修饰的原理与NSString是一致的。

** 注意 **

如果来源数组是NSMutableArray,使用copy修饰的数组会执行一次深拷贝,指向新的内存地址。但是目标数组里面内容的内存地址和来源数组里面内容的内存地址还是一致的,如果改变了来源数组里面内容,目标数组也会跟着改变。

举个例子:

** 声明 PersonModel **

@interface PersonModel : NSObject
// 姓名
@property (nonatomic, copy) NSString *name;

// 年龄
@property (nonatomic, assign) NSInteger age;

// 地址
@property (nonatomic, copy) NSString *address;
@end
** 进行 赋值 **
PersonModel *tmpFirstPerModel = [[PersonModel alloc] init];
tmpFirstPerModel.name = @"Jack";
tmpFirstPerModel.age = 16;
tmpFirstPerModel.address = @"深圳市南山区白石洲下白石一坊9巷12号";

NSLog(@"tmpFirstPerModel: %p", tmpFirstPerModel);
NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects:tmpFirstPerModel,@"2", nil];

self.strongArray = tmpArray;
self.copyedArray = tmpArray;


NSLog(@"tmpArray: %p, PersonModel: %p", tmpArray, [tmpArray objectAtIndex:0]);
NSLog(@"self.strongArray: %p, PersonModel: %p", self.strongArray, [self.strongArray objectAtIndex:0]);
NSLog(@"self.copyedArray: %p, PersonModel: %p", self.copyedArray, [self.copyedArray objectAtIndex:0]);
** 查看 输出 结果 **
tmpFirstPerModel: 0x61800002ffe0
tmpArray: 0x618000048ac0, PersonModel: 0x618000036960
self.strongArray: 0x618000048ac0, PersonModel: 0x618000036960
self.copyedArray: 0x618000036a60, PersonModel: 0x618000036960

从输出结果可以看出,虽然使用copy修饰的self.copyedArray数组进行了深拷贝,指向了新的内存地址,但是数组里面的PersonModel的指针还是和之前初始化的tmpFirstPerModel是一致的,如果你在来源数组中修改了,self.copyedArray里面的内容也会更改。

** 对personModel 进行修改**

tmpFirstPerModel.address = @"厦门市翔安区新店镇朝新路3号";

NSLog(@"tmpSecondPerModel.address: %@, %p", tmpFirstPerModel.address, tmpFirstPerModel);
NSLog(@"[self.strongArray objectAtIndex:0]: %@, %p",((PersonModel *)[self.strongArray objectAtIndex:0]).address, [tmpArray objectAtIndex:0]);
NSLog(@"[self.copyedArray objectAtIndex:0]: %@, %p",((PersonModel *)[self.copyedArray objectAtIndex:0]).address, [tmpArray objectAtIndex:0]);
** 查看 输出 结果 **
tmpSecondPerModel.address: 厦门市翔安区新店镇朝新路3号, 0x618000037020
[self.strongArray objectAtIndex:0]: 厦门市翔安区新店镇朝新路3号, 0x618000037020
[self.copyedArray objectAtIndex:0]: 厦门市翔安区新店镇朝新路3号, 0x618000037020

如果要避免这种情况,就必须对模型实现NSCopying和NSMutableCopying(如果支持可变类型)协议,然后实现- (id)copyWithZone:(NSZone *)zone 函数和- (id)mutableCopyWithZone:(NSZone *)zone ,接着遍历源数组进行copy添加到新数组中或者通过- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag函数,进行初始化拷贝。

** 代码展示 **

#pragma mark ---- NSCopying

- (id)copyWithZone:(NSZone *)zone {
    PersonModel *configModel = [[[self class] allocWithZone:zone] init];
    configModel.name = [self.name copyWithZone:zone];
    configModel.age = self.age;
    configModel.address = [self.address copyWithZone:zone];
    return configModel;
}

#pragma mark ---- NSMutableCopying

- (id)mutableCopyWithZone:(NSZone *)zone {
    return [self copyWithZone:zone];
}

// 遍历 拷贝 添加 方法
NSMutableArray *tmpMutableArray = [NSMutableArray array];
for (PersonModel *tmpPersonModel in tmpArray) {
    [tmpMutableArray addObject:[tmpPersonModel copy]];
}
self.copyedArray = tmpMutableArray;

// 初始化 方法 进行 内部 拷贝
self.copyedArray = [[NSArray alloc] initWithArray:tmpArray copyItems:YES];
樱花.jpeg

三. NSDictionary

** 声明变量 **

// strong  类型   NSDictionary
@property (nonatomic, strong) NSDictionary *strongDictionary;
// copy    类型   NSDictionary
@property (nonatomic, copy)   NSDictionary *copyedDictionary;

** NSDictionary 赋值 **

NSDictionary *tmpDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Jake",@"1000",@"LinDa",@"1001", nil];

self.strongDictionary = tmpDict;
self.copyedDictionary = tmpDict;

NSLog(@"tmpDict: %@, %p", tmpDict, tmpDict);
NSLog(@"self.strongDictionary: %@, %p", self.strongDictionary, self.strongDictionary);
NSLog(@"self.copyedDictionary: %@, %p", self.copyedDictionary, self.copyedDictionary);

** 输出 结果 **

 tmpDict: {
    1000 = Jake;
    1001 = LinDa;
}, 0x610000267040

self.strongDictionary: {
    1000 = Jake;
    1001 = LinDa;
}, 0x610000267040

self.copyedDictionary: {
    1000 = Jake;
    1001 = LinDa;
}, 0x610000267040

** NSMutableDictionary 赋值 **

NSMutableDictionary *tmpMutableDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"Jake",@"1000",@"LinDa",@"1001", nil];

self.strongDictionary = tmpMutableDict;
self.copyedDictionary = tmpMutableDict;

NSLog(@"tmpMutableDict: %@, %p", tmpMutableDict, tmpMutableDict);
NSLog(@"self.strongDictionary: %@, %p", self.strongDictionary, self.strongDictionary);
NSLog(@"self.copyedDictionary: %@, %p", self.copyedDictionary, self.copyedDictionary);

[tmpMutableDict setObject:@"Ailis" forKey:@"1002"];
NSLog(@"tmpMutableDict: %@, %p", tmpMutableDict, tmpMutableDict);
NSLog(@"self.strongDictionary: %@, %p", self.strongDictionary, self.strongDictionary);
NSLog(@"self.copyedDictionary: %@, %p", self.copyedDictionary, self.copyedDictionary);

** 输出 结果 **

 tmpMutableDict: {
    1000 = Jake;
    1001 = LinDa;
}, 0x6180000459d0

self.strongDictionary: {
    1000 = Jake;
    1001 = LinDa;
}, 0x6180000459d0

self.copyedDictionary: {
    1000 = Jake;
    1001 = LinDa;
}, 0x618000260d40


 tmpMutableDict: {
    1000 = Jake;
    1001 = LinDa;
    1002 = Ailis;
}, 0x6180000459d0

self.strongDictionary: {
    1000 = Jake;
    1001 = LinDa;
    1002 = Ailis;
}, 0x6180000459d0

 self.copyedDictionary: {
    1000 = Jake;
    1001 = LinDa;
}, 0x618000260d40

从以上测试输出结果,我们可以很明显的看出,NSDictionary用strong或者copy修饰的原理与NSString和NSArray是一致的。

同样的NSDicationary也存在着跟NSArray一样的问题。

如果来源数组是NSMutableDictionary,使用copy修饰的数组会执行一次深拷贝,指向新的内存地址。但是目标数组里面内容的内存地址和来源数组里面内容的内存地址还是一致的,如果改变了来源数组里面内容,目标数组也会跟着改变。

所以如果键值是类,要想进行内容赋值也必须实现NSCopying和NSMutableCopying(如果支持可变类型)协议,然后实现- (id)copyWithZone:(NSZone )zone 函数和- (id)mutableCopyWithZone:(NSZone )zone ,接着遍历源字典,根据key取出值进行copy添加到新字典里中或者通过- (instancetype)initWithDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary copyItems:(BOOL)flag;函数,进行初始化拷贝。

** 注意 **

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[array] = array;
for (NSMutableArray *key in dict.allKeys) {
    NSLog(@"%@", key.class);
    NSLog(@"%@", [dict[key] class]);
}

** 查看 输出 结果 **

key.class: __NSArrayI
[dict[key] class]: __NSArrayM

因为NSMutableDictionary 在 set key-value 的时候会把 key copy 一下,NSMutableArray 被 copy 成 NSArray 了,也就对应类族里面__NSArrayI。

四. 最后

送上一张喜欢的图片:

五厘米.jpeg

大家有兴趣可以看一下,如果觉得不错,麻烦给个喜欢,若发现问题请及时反馈,谢谢!

上一篇下一篇

猜你喜欢

热点阅读