高效编写代码的方法(十八):NSCopying协议

在编写程序的过程中,我们经常会想将某些对象进行复制用。在OC中,这样的复制通过copy方法实现,比如[someObject copy]。这样做的必要条件是该对象遵守NSCopying协议。

对于我们自定义的类,当我们想调用copy进行复制的时候,就需要自己来实现NSCopying协议的方法:

- (id)copyWithZone:(nullable NSZone *)zone;

NSZone

还是先理解下NSZone这个东西,在实际开发中我们几乎不会运用它。
目前我也只在2个方法中见过其作为参数存在:

+ (id)allocWithZone:(nullable NSZone *)zone;

- (id)copyWithZone:(nullable NSZone *)zone;

NSZone其实并不是一个对象,是一个C结构体,用来记录内存管理的一些信息。
我觉得可以理解为一片内存空间,在App运行的时候,系统默认分配一块Zone来管理我们这个App的所有内存中的对象,因此我们在开发中几乎不会去接触NSZone。
但是随着对象的不停alloc 和dealloc ,内存会变的碎片化,iOS为了解决这个问题,会将新对象分配到这些碎片的空隙中来进行内存填补,一般来说对象比较少的时候,这样也不会造成系统性能的亏损。但是对象一多,就不一定了。
由此引出一个问题,我们能不能专门创建一个空间,来保存一系列大量的对象,减少系统寻找“内存空隙”的时间来提高一些性能,答案是可以的。这时候NSZone就可以派上用场,把你的对象一起分配在一个自定的NSZone里面,手动管理起来就会方便很多。
NSZone的释放还不会用,估计还要切换成MRC,实在不会就不讲了。

让你的对象支持Copy

为了让你的对象支持Copy,只要两个步骤:

  • 1 声明对象遵守<NSCopying> protocol
  • 2 重写- (id)copyWithZone:(nullable NSZone *)zone;方法

对于简单的情况,我们可以如下实现:
Person.h :

#import <Foundation/Foundation.h>

@interface Person : NSObject<NSCopying>

@property (nonatomic, copy) NSString *firstName;

@property (nonatomic, copy) NSString *lastName;

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;

@end

Person.m :

#import "Person.h"

@implementation Person

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName
{
    if (self = [super init]) {
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone
{
    //直接调用init重新创建一个一模一样的对象
    return [[[self class] alloc] initWithFirstName:_firstName lastName:_lastName];
}
@end

当然以上部分只适用于简单对象的copy处理。对于复杂一点的,比如我们的person包含一个数组名为family的数组属性,这时候copyWithZone:方法又该如何重写呢?如下

- (id)copyWithZone:(NSZone *)zone
{
    Person *selfCopy = [[[self class] alloc] initWithFirstName:_firstName lastName:_lastName];
    //对特殊属性特殊Copy处理。
    [selfCopy.family = self.family mutableCopy];
    return selfCopy;
}

OC中的拷贝

mutableCopy和copy

举个例子好了:

[NSMutableArray copy] -> NSArray
[NSArray mutableCopy] ->NSMutableArray
深浅拷贝

网络上说明很多,简单来说就是是否是拷贝指针还是内存空间的重新分配。
区别在于,举个例子:如果浅拷贝,那么原本对象容器中的数据改变,那么虽然副本容器地址不会变,但是容器中的对象也随之改变了。深拷贝则不会改变。
所以具体使用哪种还是从实际情况出发。

对于对象的深浅拷贝,还是总结一下:

  • 1非容器类(NSString,NSNumber)
    直接放结论:
对象是不可变的对象:  mutableCopy ->深拷贝 -> 可变对象
                            copy ->浅拷贝 -> 不可变对象
对象是可变对象: mutableCopy ->深拷贝 -> 可变对象
                            copy ->深拷贝 -> 不可变对象
  • 2 容器类(NSArray,NSDictionary)
    对于容器类的本身,也就是对于NSArray对象来说,规则和上面的是一样的,关键是容器内的对象。
NSString *str1 = @"string1";
NSArray *array = @[str1];
NSArray *copyArray = [array copy];
NSLog(@"===========\n array:%p,  copyArray:%p\n  str1:%p  copyArray[0]:%p",array,copyArray,str1,copyArray[0]);

输出如下:

===========
 array:0x7fa90b415ed0,  copyArray:0x7fa90b415ed0**
  str1:0x10b5a1250  copyArray[0]:0x10b5a1250**
  NSString *str1 = @"string1";
  NSArray *array = @[str1];
  //注意下面这行代码的改变
  NSMutableArray *copyArray = [array mutableCopy];
  NSLog(@"===========\n array:%p,  copyArray:%p\n  str1:%p  copyArray[0]:%p",array,copyArray,str1,copyArray[0]);

输出如下:

===========
array:0x7fa37b70ef00,  copyArray:0x7fa37b758060**
str1:0x10834a250  copyArray[0]:0x10834a250**

由此可见,对于容器中的对象,mutableCopy和copy都是浅复制(可变容器也一样)。

如果涉及到容器中包含容器,那么还有深复制和完全深复制之说,完全深复制就是容器中每一层对象都是深复制,而深复制就是只复制一层的对象。如何使用都是看具体情况,不展开了。

对于任何遵守NSCopying的对象来说,实现的都应该是浅复制,因为浅复制使用是最多的。

总结

  • 1 想让你的对象支持复制的话,遵守NSCopying协议并实现copyWithZone:方法。
  • 2 如果你的对象支持不可变和可变两种分类,那么需要实现mutableCopy和copy两种方法
  • 3 区分深浅拷贝并看情况使用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容