博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于copy, mutableCopy, 浅拷贝,深拷贝
阅读量:4114 次
发布时间:2019-05-25

本文共 6822 字,大约阅读时间需要 22 分钟。

随便写一个类, 继承自NSObject, 

.h文件

@interface YIOHOn :NSObject

@property (nonatomic,strong)NSString *myName;

@end

.m文件:

@implementation YIOHOn

@end

使用时:

    YIOHOn *onObj = [[YIOHOnalloc]init];

    onObj.myName =@"Jacky";

   YIOHOn *cop1 = [onObjcopy];

   YIOHOn *cop2 = [onObjmutableCopy];

    NSLog(@"cop1=%@, cop2=%@", cop1, cop2);

运行情况是, 运行到copy时就会崩溃,崩溃显示的信息如下:

2013-10-24 12:31:49.405 YOnsgongs[20395:a0b] -[YIOHOn copyWithZone:]: unrecognized selector sent to instance 0xa109450

2013-10-24 12:31:49.435 YOnsgongs[20395:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[YIOHOn copyWithZone:]: unrecognized selector sent to instance 0xa109450'

*** First throw call stack:

分析发现, 这里崩溃是在YIOHOn类方法copyWithZone中崩溃的。可是我们在程序中只调用了copy, 哪里有copyWithZone:呢?

1, 我们知道咱们的YIOHOn类是继续自NSObject的, 我们要以看到NSObject.h文件中, 如下:

@interface NSObject <NSObject> {

    Class isa  OBJC_ISA_AVAILABILITY;

}

+ (void)load;

+ (id)new;

+ (id)allocWithZone:(struct_NSZone *)zone;

- (id)copy;

- (id)mutableCopy;

+ (id)copyWithZone:(struct_NSZone *)zoneOBJC_ARC_UNAVAILABLE;

+ (id)mutableCopyWithZone:(struct_NSZone *)zoneOBJC_ARC_UNAVAILABLE;

很明显,我们能执行
[onObj 
copy
]是因为NSObject有对象方法, -(id)copy; 。 然后我们到帮助文档中看copy解释

This is a convenience method for classes that adopt the  protocol. An exception is raised if there is no implementation for.

说这个方法是为那些类,这些类实现了NSCopying的协议的类,如果这个类没有实现copyWithZone:方法为这个协议,那么就会抛出异常崩溃。

因为我们调用了copy方法,而copy方法最终会要求调用类方法copyWithZone:, 而NSObject本身并没有实现这个类方法, 这个类方法是放在NSCopying协议中的, 虽然在上面的NSObject.h文件中, 有提到NSObject有类方法copyWithZone:, 其实这是一个误导, 对NSObject, 这里虽然写了一个+(id)copyWithZone:但NSObject类本身却并没有实现这个类方法, 它是要求子类去实现的, 子类如果要调用copy方法, 那么子类就去遵循NSCopying协议, 然后就能正常调用了。

NSMutableCopying协议同理

讨论一下不可变拷贝与可变拷贝

先上代码

    NSString *originString =@"China";

   NSString *muString = [originStringmutableCopy];

    [(NSMutableString *)muStringappendString:@"Love"];

    NSLog(@"after append, muString=%@", muString);

执行结果, 

after append, muString=ChinaLove

分析之, 可以得出结论,对执行mutableCopy后,不管原始值是可变的还是不可变后, 执行后生成的对象一定是可变的。

再上代码:

    NSMutableString *oriMutString = [NSMutableStringstringWithFormat:@"You"];

   NSString *genString = [oriMutStringcopy];

    [(NSMutableString *)genStringappendFormat:@"LikeMe"];

   NSLog(@"genString=%@", genString);

运行会发现崩溃,崩溃信息为:

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'

分析之, 得出结论,执行copy后,不管原始值是可变的还是不可变的, 生成的对象一定是不可变的。

深拷贝与浅拷贝

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

    

   YYCar *copiedCar = [[YYCarallocWithZone:zone]init];

    copiedCar.bandName = [self.bandNamecopyWithZone:zone];

    copiedCar.soldPrice = self.soldPrice;

    return copiedCar;

}

深拷贝与浅拷贝与copy和mutableCopy无关。copy和mutableCopy中的实现方法有关。

上述方法中, copiedCar.bandName = [self.bandName copyWithZone:zone];就是一个深拷贝,因为生成的bandName与原始的self.bandName内存地址变了。 

而如果写成copiedCar.bandName =self.bandName;则是浅拷贝,因为他们的内存地址是相同的。

结论:在拷贝时, 如果内容本来是不可变的, 如上面这个bandName,是NSString类型的, 那么建议使用浅拷贝的方式,这样的话, 不用太重新创建对象。 因为在创建对象的时候会花费较多的执行时间,而创建出的对象因为是不可变的, 其实就是一个重复的对象,意义并不大。而对于NSMutableString来说,尽量采用创建新对象的方式来生成, 以防止以前的对象与拷贝后的对象修改时影响对方的值。

最后再说一下嵌套深层拷贝的问题

先上代码:

    NSDictionary *dic = [NSDictionarydictionaryWithObjectsAndKeys:@"Jacky",@"Name",

                            [NSArrayarrayWithObjects:@"1",@"2",nil],@"CheckDic",

                            [NSArrayarrayWithObjects:@"First",@"Second",nil],@"TheArr"nil];

    NSMutableDictionary *mutDic = [dicmutableCopy];

    

    //改直接下面的键值

    [mutDicsetObject:@"Zouzou"forKey:@"Name"];

    

    //改直接下面的键值对数组

    [mutDic setObject:[NSArrayarrayWithObjects:@"11",@"22",nil] forKey:@"CheckDic"];

    

    NSLog(@"before dic=%@,  \n mutDic=%@", dic, mutDic);

    

    // 改内置的Arr

    NSMutableArray *mutArr = (NSMutableArray *)[mutDicobjectForKey:@"TheArr"];

    [mutArraddObject:@"Third"];

    

    NSLog(@"after dic=%@,  \n mutDic=%@", dic, mutDic);

运行时,会发现输入为:

before dic={

    CheckDic =     (

        1,

        2

    );

    Name = Jacky;

    TheArr =     (

        First,

        Second

    );

},  

 mutDic={

    CheckDic =     (

        11,

        22

    );

    Name = Zouzou;

    TheArr =     (

        First,

        Second

    );

}

2013-10-24 15:07:37.842 YOnsgongs[20953:a0b] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0xa8550d0

2013-10-24 15:07:37.844 YOnsgongs[20953:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0xa8550d0'

*** First throw call stack:

然后就崩溃了, 崩溃的原因我们可以想到, 这里的
mutDic确实是可变的, 但是它下面的键值对应的数据却不会是可变的。 所以我们往其内嵌的数组中添加数据,则会引发崩溃。
简单地说, 就是上面只执行了一层的可变拷贝, 更深层的则没进行进行可变拷贝。 
那么如何能做到更深层次的可变拷贝呢。
使用下面这个类别, 就可做到。
.h文件如下

@interface NSDictionary(MutableDeepCopy)

// NSDictionary深拷贝方法

- (NSMutableDictionary *)mutableDeepCopyOfDictionary;

@end

@interface NSArray(MutableDeepCopy)

// NSArray深拷贝方法

- (NSMutableArray *)mutableDeepCopyOfArray;

@end

实现文件

#import "MutableDeepCopy.h"

@implementation NSDictionary(MutableDeepCopy)

- (NSMutableDictionary *) mutableDeepCopyOfDictionary {

NSMutableDictionary *newDict = [NSMutableDictionarydictionaryWithCapacity:[selfcount]];

NSArray *keys = [selfallKeys];

for (id keyin keys) {

id oneValue = [selfvalueForKey:key];

id oneCopy =nil;

if ([oneValuerespondsToSelector:@selector(mutableDeepCopyOfDictionary)]) {

oneCopy = [[oneValuemutableDeepCopyOfDictionary]retain];

}

elseif ([oneValuerespondsToSelector:@selector(mutableDeepCopyOfArray)]) {

oneCopy = [[oneValuemutableDeepCopyOfArray]retain];

}

elseif ([oneValueconformsToProtocol:@protocol(NSMutableCopying)]) {

            oneCopy = [oneValuemutableCopy];

}

        

if (oneCopy ==nil) {

oneCopy = [oneValuecopy];

}

[newDictsetObject:oneCopyforKey:key];

        [oneCopyrelease];

}

return newDict;

}

@end

@implementation NSArray(MutableDeepCopy)

- (NSMutableArray *)mutableDeepCopyOfArray {

NSMutableArray *newArray = [NSMutableArrayarrayWithCapacity:[selfcount]];

for (int i = 0; i < [selfcount]; i++) {

id oneValue = [selfobjectAtIndex:i];

id oneCopy =nil;

if ([oneValuerespondsToSelector:@selector(mutableDeepCopyOfArray)]) {

oneCopy = [[oneValuemutableDeepCopyOfArray]retain];

}

elseif ([oneValuerespondsToSelector:@selector(mutableDeepCopyOfDictionary)]) {

oneCopy = [[oneValuemutableDeepCopyOfDictionary]retain];

}

elseif ([oneValueconformsToProtocol:@protocol(NSMutableCopying)]) {

            oneCopy = [oneValuemutableCopy];

}

if (oneCopy ==nil) {

oneCopy = [oneValuecopy];

}

[newArrayaddObject:oneCopy];

        [oneCopyrelease];

}

return newArray;

}

@end

// 使用时需要注意, 上面这个类别使用的是非ARC的。

通过上面这种全深次的拷贝, 出来的东东就是彻底的深拷贝, 所有的对象,都是mutable的

输出如下:

after dic={

    CheckDic =     (

        1,

        2

    );

    Name = Jacky;

    TheArr =     (

        First,

        Second

    );

},  

 mutDic={

    CheckDic =     (

        11,

        22

    );

    Name = Zouzou;

    TheArr =     (

        First,

        Second,

        Third

    );

}

heqin补充:

被充一个深拷贝的方式,使用存档和取档的方式进行,相当于是把当前的内存转成NSData封存,然后再使用解档的方式从NSData生成对象,则生成的对象与原对象完全无关。真正意义上的深层次拷贝。

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil];
    NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
    NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];

转载地址:http://xtwpi.baihongyu.com/

你可能感兴趣的文章
指针与数组的总结
查看>>
自定义类型
查看>>
初识Linux
查看>>
网络基础思维导图
查看>>
排序算法之冒泡排序(C语言实现)
查看>>
c++命名空间 ,输入与输出 ,缺省参数
查看>>
C++函数重载
查看>>
c++引用
查看>>
标准c库中的读写表示
查看>>
stdio.h
查看>>
练习题---哈希列表
查看>>
Linux中cat、more、less命令区别
查看>>
练习题---循环队列
查看>>
Linux查看负载命令
查看>>
编程--平衡二叉树
查看>>
传输层——tcp和udp的区别
查看>>
编程--二叉树的下一个节点
查看>>
常用数据类型对应字节数
查看>>
练习题---普通二叉树
查看>>
网络那些词
查看>>