copy和mutableCopy

如何让自定义对象支持 copy 操作?是重写 copy 方法么?当然不是,而是需要让自定义类实现 NSCopying 协议,该协议只有一个方法:

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

以前开发程序时,会把内存分成不同的“区”,而对象会创建在某个区里。现在不用了,每个程序只有一个“默认区”(default zone),实现该方法时,不必担心其中的 zone 参数。例如,我们要让自定义的 ACLStudent 类支持拷贝功能,则可以像如下这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

ACLStudent.h

@interface ACLStudent : NSObject

@property (nonatomic, assign, readonly) NSInteger studentId;

@property (nonatomic, copy, readonly) NSString *firstName;

@property (nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName;

@end

-----

ACLStudent.m

@implementation ACLStudent

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName {

    self = [super init];

    if (self) {

        _studentId = studentId;

        _firstName = [firstName copy];

        _lastName = [lastName copy];

    }


    return self;

}

- (instancetype)copyWithZone:(NSZone *)zone {

    ACLStudent *copy = [[[self class] allocWithZone:zone] initWithStudentId:_studentId firstName:_firstName lastName:_lastName];


    return copy;

}

+ (BOOL)accessInstanceVariablesDirectly {

    return YES;

}

@end

上例中 copyWithZone: 方法使用了全能初始化方法(designated initializer)来执行拷贝对象的所有初始化工作。这里需要注意,对于未能在全能初始化方法中设置好的变量,需要在 copyWithZone: 方法中做特殊处理。例如,如果在 ACLStudent 类中增加一个变量,来存储当前学生所选选修课的科目,完整代码变成如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

ACLStudent.h

@class ACLCourse;

@interface ACLStudent : NSObject

@property (nonatomic, assign, readonly) NSInteger studentId;

@property (nonatomic, copy, readonly) NSString *firstName;

@property (nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName;

- (void)addElective:(ACLCourse *)course;

- (void)removeElective:(ACLCourse *)course;

@end

--------

@implementation ACLStudent {

    NSMutableSet *_electives;  /**< 选修科目 */

}

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName {

    self = [super init];

    if (self) {

        _studentId = studentId;

        _firstName = [firstName copy];

        _lastName = [lastName copy];


        _electives = [NSMutableSet new];

    }


    return self;

}

- (void)addElective:(ACLCourse *)course {

    [_electives addObject:course];

}

- (void)removeElective:(ACLCourse *)course {

    [_electives removeObject:course];

}

- (instancetype)copyWithZone:(NSZone *)zone {

    ACLStudent *copy = [[[self class] allocWithZone:zone] initWithStudentId:_studentId firstName:_firstName lastName:_lastName];

    copy->_electives = [[NSMutableSet alloc] initWithSet:_electives copyItems:YES];


    return copy;

}

+ (BOOL)accessInstanceVariablesDirectly {

    return NO;

}

@end

注意全能初始化方法和 copyWithZone: 方法实现的变化, 这里我们对 _electives 对象执行的是深拷贝。

假如我们想让自定义对象支持 mutableCopy 操作,那又应该怎么操作呢?这需要自定义对象遵循 NSMutableCopying 协议, 该协议也只有一个方法:

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

其与 copy 相似,也是使用默认的 zone 参数来调用 mutableCopyWithZone:。

如果类分为可变版本和不可变版本,那么就应该实现 NSMutableCopying, 且在可变类中覆写 copyWithZone: 方法时,应该返回一份不可变的版本。无论当前实例是否可变,若需获取其可变版本的拷贝,均应调用 mutableCopy 方法,若需不可变的拷贝,则总应该调用 copy 方法。例如对于不可变的 NSArray 和可变的 NSMutableArray,以下关系总是成立:

1

2

- [NSMutableArray copy] => NSArray

- [NSArray mutableCopy] => NSMutableArray

如何对 NSArray 执行深拷贝呢?苹果官方文档 Copying Collections 提供了以下两种方法。

方法1:

1

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];

这种方式执行拷贝时,someArray 中的可变对象是执行深拷贝,而对于不可变对象,仍然执行的是浅拷贝。

方法2:

1

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

这种方式实现的是真正意义上的深拷贝,oldArray 中所有元素都是深拷贝。

那么 copy 和 mutableCopy 返回的对象是执行的深拷贝还是浅拷贝呢?这篇博客 [copy与mutableCopy][] 对系统对象执行 copy 和 mutableCopy 时到底执行的深拷贝还是浅拷贝进行了讨论。其中系统对象分为了两类:

系统的非容器类对象,如 NSString、NSMutableString、NSNumber 等。

系统的容器类对象,如 NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等。

现把结论摘抄如下:

对于系统的非容器类对象,如果对一不可变对象(如 NSString)复制,copy 是指针复制(浅拷贝)和 mutableCopy 就是对象复制(深拷贝); 如果是对可变对象(如 NSMutableString)复制,copy 和 mutableCopy 都是深拷贝,但是 copy 返回的对象是不可变的。

对于系统的容器类对象,对不可变对象(如 NSArray)进行复制,copy 是指针复制(浅拷贝), mutableCopy 是对象复制(深拷贝), 但是不管是 copy 还是 mutableCopy, 且不论容器内对象是可变还是不可变,返回的容器内对象都是指针复制(浅拷贝)。

对于系统的容器类对象,对可变对象(如 NSMutableArray)进行复制时,copy 和 mutableCopy都是对象复制(深拷贝),但是不管是 copy 还是 mutableCopy,且不论容器内对象是可变还是不可变,返回的容器内对象都是指针复制(浅拷贝)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容