1、契机
程序员总是不喜欢记忆,研究出来的东西,以后靠着经验或者直觉这么做了,或许还是修炼的不够到家没有融会贯通。
2、copy官方文档原话:
Discussion
This is a convenience method for classes that adopt the NSCopying protocol. An exception is raised if there is no implementation for copyWithZone:.
NSObject
does not itself support the NSCopying protocol. Subclasses must support the protocol and implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super
first, to incorporate its implementation, unless the subclass descends directly from NSObject
.
Returns:
The object returned by the NSCopying protocol method copyWithZone:,.
中式翻译一下
对于采用NSCopying协议的类,这是一种方便的方法。如果没有copyWithZone:的实现,则会引发异常。
NSObject本身不支持NSCopying协议。子类必须支持协议并实现copyWithZone:方法。copyWithZone:方法的子类版本应该首先将消息发送给super,以合并它的实现,除非子类直接从NSObject派生。
返回的一个对象时由NSCopying协议方法copyWithZone:产生的。
3、开始手痒
定义三个属性
@property (nonatomic, copy) NSMutableArray *arr;
@property (nonatomic, strong) NSMutableArray *bArr;
@property (nonatomic, copy) NSMutableArray *cArr;
懒加载一下
- (NSMutableArray *)arr {
if (!_arr) {
_arr = [NSMutableArray array];
}
return _arr;
}
- (NSMutableArray *)bArr {
if (!_bArr) {
_bArr = [NSMutableArray array];
}
return _bArr;
}
- (NSMutableArray *)cArr {
if (!_cArr) {
_cArr = [NSMutableArray array];
}
return _cArr;
}
好戏开始了
1、我们将一个copy修饰和一个strong修饰的NSMutableArray分别进行copy和mutableCopy操作,结果发现堆地址不一样了。开始进行自问自答环节,说明了什么?
viewDidLoad里面
[self.arr addObject:@"1"];
[self.bArr addObject:@"1"];
[self.bArr addObject:@"2"];
[self.cArr addObject:@"1"];
[self.cArr addObject:@"2"];
copy修饰的arr执行copy和mutableCopy
NSMutableArray *arr1 = [self.arr copy];
NSMutableArray *arr2 = [self.arr mutableCopy];
NSLog(@"\n self.arr:%p \n arr1:%p \n arr2:%p", self.arr, arr1, arr2);
2、下面是堆地址结果
//self.arr:0x1c0047d70
// arr1:0x1c00034d0
// arr2:0x1c025e990
3、下面是copy和mutableCopy之后的实际类
(lldb) po [arr1 class]
__NSSingleObjectArrayI
(lldb) po [arr2 class]
__NSArrayM
(lldb) po [self.arr class]
__NSArrayM
(lldb)
发现点什么
a、由于NSString继承的NScopying协议,这里数组和堆地址存的字符串都是是一个深拷贝。
b、copy操作之后产生的是一个不可变数组,mutableCopy之后产生的是一个可变的数组。
4、继续开始,strong修饰的bArr
NSMutableArray *arr3 = [self.bArr copy];
NSMutableArray *arr4 = [self.bArr mutableCopy];
NSLog(@"\n self.bArr:%p \n arr3:%p \n arr4:%p", self.bArr, arr3, arr4);
//self.bArr:0x1c025e960
// arr3:0x1c00269c0
// arr4:0x1c025ecf0
又发现点什么
a、这个数组和字符串还是一个深拷贝
b、这个结论跟上面一样
5、上面的打印中为什么有__NSSingleObjectArrayI类
NSMutableArray *arr5 = [self.cArr copy];
NSMutableArray *arr6 = [self.cArr mutableCopy];
NSLog(@"\n self.cArr:%p \n arr5:%p \n arr6:%p", self.cArr, arr5, arr6);
//self.cArr:0x1c025ed20
// arr5:0x1c0037380
// arr6:0x1c025e9f0
打印他们的类
(lldb) po [self.cArr class]
__NSArrayM
(lldb) po [arr5 class]
__NSArrayI
(lldb) po [arr6 class]
__NSArrayM
(lldb)
发现
a、只有一个元素的时候拷贝成了这么一个玩意__NSSingleObjectArrayI,多个元素的时候是一个__NSArrayI(这里涉及到类簇了,看到过这么一篇文章[https://www.aopod.com/2017/02/24/class-clusters/])
6、上面说了copy方法和mutableCopy;继续copy修饰和strong修饰的区别请看下面
NSMutableArray *arr7 = [NSMutableArray array];
self.bArr = arr7;
NSLog(@"\n self.bArr:%p \n arr7:%p", self.bArr, arr7);
//self.bArr:0x1c025e930
// arr7:0x1c025e930
self.cArr = arr7;
NSLog(@"\n self.cArr:%p \n arr7:%p", self.cArr, arr7);
//self.cArr:0x1c0002770
// arr7:0x1c025e930
又发现点什么
a、strong修饰的是引用型,原对象的引用计数加1。
b、copy修饰的属性会给它赋值的时候拷贝一份,堆地址发生变化。
4、有始有终,那我们来看一看NSArray的copy和mutableCopy是什么情况。
NSArray *arr8 = @[@"1", @"2"];
NSArray *copyArr = [arr8 copy];
NSMutableArray *mCopyArr = [arr8 mutableCopy];
//(__NSArrayI *) $11 = 0x00000001c003a9e0 @"2 elements"
//(__NSArrayI *) $12 = 0x00000001c003a9e0 @"2 elements"
//(__NSArrayM *) $13 = 0x00000001c045ac10 @"2 elements"
对于NSArray,copy是浅拷贝,mutableCopy是深拷贝(深拷贝的层数基于元素是否实现copyWithZone:)
对于NSMutableArray,copy和mutableCopy都是实现的深拷贝(深拷贝的层数基于元素是否实现copyWithZone:)
关于执行拷贝方法之后得到的类:执行copy方法之后,得到的是一个NSArray类型(这里不谈引用+1,还是重新分配堆地址),执行mutableCopy得到的是一个NSMutableArray类型。
NSMutableArray *mutabArr = [NSMutableArray arrayWithObjects:@"1", @"2", nil];
NSArray *copyMArr = [mutabArr copy];
NSMutableArray *mCopyMArr = [mutabArr mutableCopy];
(lldb) p mutabArr
(__NSArrayM *) $15 = 0x00000001c045ad90 @"2 elements"
(lldb) p copyMArr
(__NSArrayI *) $16 = 0x00000001c003abc0 @"2 elements"
(lldb) p mCopyMArr
(__NSArrayM *) $17 = 0x00000001c045b000 @"2 elements"
(lldb)
网友评论