《关于我学习OC——对象复制这件事》

对象复制

copy与mutableCopy方法

copy方法用于复制对象的副本。通常,copy方法总是返回对象的不可修改的副本
即使对象本身是可修改的(程序调用NSMutableString的copy方法,仍热返回不可修改的字符串对象)
mutableCopy方法总是返回对象的可修改副本(NSString的mutableCopy方法将会返回一个NSMutableString对象)
copy和mutablecopy返回的总是原对象的副本,程序对复制的副本进行修改,原对象通常都不会受到影响

NSCopying & NSMutableCopy Protocol

我们自定义的类是否可以调用copy与mutableCopy方法复制副本:可以,但要有所改变
观察下面文件,我们尝试调用FKDOG对象的copy方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果会报错
在这里插入图片描述

以上错误提示,我们的自定义类找不到copywithzone方法,可是我们并未调用copywithzone方法,伊苏尔德,这是为何?
NSObject提供好copy方法,但是自定义类并不能直接调用其方法来复制自身。当程序调用对象的copy方法来复制自身时,底层需要调用copywithzone方法来完成实际的复制工作,copy的返回值就是copywithzone方法的返回值。
我们一般采用一些手段来让我们自定义的类可以调用copy/mutableCopy方法

  • 让该类实现NSCopy/NSMutableCopying协议
  • 让该类实现copyWithZone/mutableCopyWithZone方法

Like This
在这里插入图片描述
在这里插入图片描述
这样子就不会报错了
在这里插入图片描述
我们在该方法中重新创建了一个FKDOG对象并让该FKDOG对象的所有属性值与被复制对象的属性相等,最后返回这个新创建的对象(其副本)
对于copy调用copywithzone的说法可以验证一下我们把实现部分改成这个样子
在这里插入图片描述

再运行一下就会得到这样的结果:
在这里插入图片描述
就是简单的一直在循环
在这里插入图片描述
主函数去调用copy
copy去调用copywithzone,重写的copywithzone再去调用copy,达成一个循环
当然,如果重写copywithzone方法时,其父类已经实现NSCopying协议,并重写过copywithzone方法,那么子类重写的copywithzone方法先调用父类的copy方法,复制从父类继承得到的成员变量,然后对子类中定义的成员变量进行赋值。
假如父类已经重写过copywithzone方法,那么子类重写copywithzone方法就可以用验证的方法去写,不会出现循环(如果你父类重写的没问题的话)

浅(shallow)复制与深(deep)复制

浅复制

被复制对象的所有变量都含有与原来对象相同的值,而所有其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
两个对象的指针变量将会指向同一个对象,依然存在公共部分。

深复制

被复制对象的所有变量都含有与原来对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制的新对象,而不再是原有的那些被引用的对象。换言之,深复制把复制的对象所引用的对象都复制了一遍。新对象和原对象不共享内存
在这里插入图片描述接着上一节的代码,我们对主函数做出修改在这里插入图片描述

运行的到这个结果,可真是奇怪哦
在这里插入图片描述
明明我们只修改 狗2 的name属性值,为何 狗1 也被改了?这个就是浅复制
浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制会采用不同的方式,不仅会复制对象本身,且会“递归”复制每个指针的实例变量,直至两个对象没有任何公共部分

- (id) copyWithZone: (NSZone*)zone {
    NSLog(@"********");
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    //将原对象的name实例变量复制一份副本后复制给新对象的name 实例变量
    dog.name = [self.name mutableCopy];
    dog.age = self.age;
    return dog;
}

这里我们将原对象的name实例变量复制了一份可变副本,再将可变副本的值赋给新对象name的实例变量,这样就保证两个对象间没有公共部分,这就深复制了。
一般来说,深复制的实现难度大很多,尤其是当该对象包含大量的指针类型的实例变量时,如果某些实例变量里再次包含指针类型的实例变量,那么实现深复制就会更加复杂
在这里插入图片描述

Foundation框架中的类,大部分都只是实现了浅复制。

setter方法的复制选项

在合成setter&getter方法时可以使用copy指示符,当程序调用setter方法时,实际上是将传入参数的副本赋给程序的实例变量。

#import <Foundation/Foundation.h>
@interface FKShift : NSObject <NSCopying>
@property (nonatomic,copy) NSMutableString * name;
@end
#import <Foundation/Foundation.h>
#import "FKShift.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKShift * shift = [[FKShift alloc] init];
        shift.name = [NSMutableString stringWithString:@"zzy&xjn"];
        [shift.name appendString:@"12346p"];
        //添加字符串
    }
    return 0;
}

这样运行的话,不出意外会直接报错
错误提示不允许修改shift的name属性值
程序定义name属性时使用了copy指示符,该指示符会调用setName方法,程序会使用参数的副本对name实例变量复制


- (void) setName: (NSMutableString*) aname {
    name = [aname copy];
}

copy方法我们也知道默认返回不可变副本,因此程序在赋值给name的实例变量依然是不可变字符串

定义合成setter&getter方法时,没有提供mutableCopy指示符,即定义实例变量时使用的即是可变类型,但是只要使用了copy指示符,那么得到的就是不可变的对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值