copy 和 mutablecopy 到底是怎么回事儿?
原则其实还是这样:所有的设计都是为了功能方便!这两个方法的实现意义大概在两个点:
添加这两个方法,实现了NSString,NSArray 等OC基本数据类型的拷贝操作。
提供一套代理协议模式的设计应用,保留了一套方便的接口,方便用户自定义OC对象拷贝的实现。
1. 先看Foundation 框架下NSObject.h文件中声明了两个协议:NSCopying 和 NSMutableCopying
/*************** Basic protocols ***************/
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
(1). 其实在真实的开发中,一般来说,真正添加这两个协议来实现拷贝的对象的需求可能不多!而我们最常用的基本OC对象(字符串,数组,集合,字典,NSData ,NSURL,NSIndeSet 等)默认是遵循实现了两个协议或其中一个协议的。看看NSString 和 NSArray 头文件就知道了:
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
…………
(2). 和NSString、NSArray一样,一个自定义对象实现拷贝功能,所依赖的方法是copy和这mutablecopy,那么久必须—>对应的拷贝对象(NSObject的子类)需要遵循以上两个协议(<NSCopying, NSMutableCopying>)。由此可知,代理模式在OC的语言设计中真的是无处不在呀!
…………
(3). 注意,就算是自定义对象MyObj 遵循了协议,实现协议方法(copyWithZone:| mutableCopyWithZone:)确实要靠自己来写,最后的效果就是以coder的意志做转移的了。但是最好不要这么任性,该copy的还是copy一个给返回,该mutable copy的mutablecopy一份返回,体现这一点可以通过亲自测试,看第二条
2. 以NSString 和 NSArray 为例看看效果, 从而知道copy 和mutablecopy在实际使用中的效果。
NSMutableString *str = [NSMutableString stringWithString:@"123"];
NSString *strCopy = [str copy]; /// 返回不可变,
NSMutableString *mustrCopy = [str mutableCopy]; /// 返回可变
NSLog(@"\n%p->%@, %p->%@, %p->%@",&str,str,&strCopy,strCopy,&mustrCopy,mustrCopy);
// 0x7fff5fbff7d8->123, 0x7fff5fbff7d0->123, 0x7fff5fbff7c8->123
[mustrCopy appendString:@"z"];
str =(NSMutableString *) @"321"; /// 注意,和C语言不一样,@出来的东西可是对象,不能实现强转!str将成为不可变。
// [str appendString:@"a"]; /// 崩溃,原因就是这里(NSMutableString *)不是强转,
str = [NSMutableString stringWithFormat:@"321"];
[str appendString:@"a"];
[[mustrCopy copy] appendString:@"a"]; /// 崩溃, [mustrCopy copy]得到的是不可变字符串
NSMutableArray *muArr = [NSMutableArray arrayWithObjects:str,strCopy,mustrCopy, nil];
NSArray *arrCopy = [muArr copy];
NSMutableArray *muArrCopy = [arrCopy mutableCopy];
NSLog(@"\n%p->%@, %p->%@, %p->%@",&muArr,muArr[0],&arrCopy,arrCopy[1],&muArrCopy,muArrCopy[2]);
// 0x7fff5fbff7c0->321a, 0x7fff5fbff7b8->123, 0x7fff5fbff7b0->123z
[muArr[0] appendString:@"b"];
[muArrCopy[2] appendString:@"y"];
NSLog(@"\n%p->%@, %p->%@, %p->%@",&muArr,muArr[0],&arrCopy,arrCopy[1],&muArrCopy,muArrCopy[2]);
NSLog(@"\n%p->%@, %p->%@, %p->%@",&str,str,&strCopy,strCopy,&mustrCopy,mustrCopy);
// 0x7fff5fbff788->321ab, 0x7fff5fbff780->123, 0x7fff5fbff778->123zy
// 0x7fff5fbff7a0->321ab, 0x7fff5fbff798->123, 0x7fff5fbff790->123zy
strCopy = @"UHB";
[mustrCopy appendString:@"YGV"];
NSLog(@"\n%p->%@, %p->%@, %p->%@",&muArr,muArr[0],&arrCopy,arrCopy[1],&muArrCopy,muArrCopy[2]);
NSLog(@"\n%p->%@, %p->%@, %p->%@",&str,str,&strCopy,strCopy,&mustrCopy,mustrCopy);
// 0x7fff5fbff788->321ab, 0x7fff5fbff780->123, 0x7fff5fbff778->123zyYGV
// 0x7fff5fbff7a0->321ab, 0x7fff5fbff798->UHB, 0x7fff5fbff790->123zyYGV
这个地方可以看出,数组中的可变字符串其实并没有copy新建,而是指向原始可变字符串。
(1) . 只看copy 和 mutablecopy,就我个人目前的意识来看,在OC基本对象数据类型中比较有意义,毕竟一切都是为了方便使用嘛! 对于这些OC基本类型,对于存在mutable和imutable的, copy 和mutablecopy的意义就特别明显了。不过其中存在一些注意点:
a. copy 和 mutablecopy都是创建了一个新的对象,copy的不可变,mutablecopy的可变。
b. 对于数组,数组中的成员如果是可变的,则是原有对象指针拷贝,如果是不可变的,则是内容拷贝。字典同理
c. copy 和 mutablecopy 如果是使用于OC的基本对象数据类型的时候,规律是恒定的。基本如上,可以将NSString理解为最原子的对象。
(2). 上面是讨论OC的基本对象数据类型,线面 新建一个 Person类,看看自定义的对象如何使用 copy 和 mutablecopy 。
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *title;
@end
@implementation Person
- (NSString *)description
{
return [NSString stringWithFormat:@"name: %@, age: %ld, title: %@",_name,(long)_age,_title];
}
- (id)copyWithZone:(NSZone *)zone
{
Person *person = [Person new];
person.age = self.age;
person.name = self.name;
person.title = self.title;
return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
// mutablecopy 的处理方法,全由意志处理并不好!
// zone 这是一个表示管理内存的标示,且不管它
return self;
}
@end
Person *_person = [[Person alloc] init];
Person *_personCopy = _person;
[_person setValuesForKeysWithDictionary:@{
@"name" : @"小名",
@"age" : @26,
@"title" : @"CEO"
}];
NSLog(@"\n %p -> %@ | %p -> %@",&_person, _person, &_personCopy, _personCopy);
// 0x7fff5fbff770 -> name: 小名, age: 26, title: CEO | 0x7fff5fbff768 -> name: 小名, age: 26, title: CEO
_personCopy = [_person copy];/// 想要不崩溃, 需遵循NSCoping协议,并实现协议方法 copyWithZone:
_personCopy.name = @"小明";
NSLog(@"\n %p -> %@ | %p -> %@",&_person, _person, &_personCopy, _personCopy);
// 0x7fff5fbff770 -> name: 小名, age: 26, title: CEO | 0x7fff5fbff768 -> name: 小明, age: 26, title: CEO
_person.name = @"小张";
_personCopy = [_person mutableCopy];
NSLog(@"\n %p -> %@ | %p -> %@",&_person, _person, &_personCopy, _personCopy);
// 0x7fff5fbff770 -> name: 小张, age: 26, title: CEO | 0x7fff5fbff768 -> name: 小张, age: 26, title: CEO
_结语:
copy 和 mutablecopy 是两个写在 NSObject中的方法,是利用协议代理方法保留一套接口,OC 实现了(字符串,数组,集合,字典,NSData ,NSURL,NSIndeSet )这些基本OC对象数据类型的接口方法,对于其他的自定义OC对象,要使用copy 和mutablecopy方法需要手动添加协议(<NSCopying,NSMutableCopying>),并实现两个协议的required方法(mutableCopyWithZone: | copyWithZone:)。
协议方法中必定需要返回一个 对象类型,具体怎么处理权限上就是任coder的意志决定,然而此处最好准许OC基本对象数据类型的规律。