Objective-C 深复制和浅复制与<NSCopying>协议

本文深入解析了深复制和浅复制的概念,并通过Objective-C的具体案例详细介绍了如何实现对象的复制,包括不可变副本和可变副本的创建过程。

1.简单复制只能实现浅拷贝:指针赋值,使两个指针指向相同的一块内存空间,操作不安全。

2. Foundation类已经遵守了<NSCopying>和 <NSMutableCopying>协议,即实现了copy和mutableCopy方法,因此Foundation对象可以使用这些方法创建对象的副本或可变副本

@protocol NSCopying

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

@end

@protocol NSMutableCopying

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

@end


3.用户自定义类遵守<NSCopying>协议和<NSMutableCopying>协议,则必须实现copyWithZone方法和mutableCopyWithZone方法,否则该类对象无法响应copy和mutableCopy消息 

4.实现copyWithZone方法,例:

       

-(id)copyWithZone:(NSZone *)zone

{

         Student *stu = [[Student allocWithZone:zone]initWithName:self.name Age:self.age];

         return stu;

}

对应main函数中:假设已经有一个Student对象stu1;

则:Student stu2 = [stu1 copy];

实现stu2是stu1的副本,这里是深复制,stu1和stu2分别对应不同内存。

 

5. 如果你的类产生了子类,那么copyWithZone:方法也将

被继承

Student *stu = [[Student allocWithZone: zone] init];

 该方法应该改为: Student *stu = [[[self class] allocWithZone: zone]init];

 如果编写一个类的copyWithZone:方法那么子类的方法应该先调用父类的copy方法以复制继承来的copy实例变量.


1 NSCopying与NSMutableCopying协议

用copy方法能得到字符串的不可变副本,而mutableCopy方法能得到字符串的可变副本。 

但是如果对自定义的类,我们不能直接使用copy和mutableCopy方法,需要让类遵守NSCopying与NSMutableCopying协议,然后实现继承自协议的copyWithZone:方法和mutableCopyWithZone:方法,这两个方法返回的是对象本身,得到一个对象的副本。

总之,为了保证一个自定义的类的对象使用copy方法产生一个不可变的副本,需要做两步: 

1. 让该类继承NSCopying协议。 

2. 在类的代码实现部分重写继承自NSCopying协议的CopyWithZone:方法,该方法返回一个该类的不可变对象副本。

为了保证一个自定义的类的对象使用mutableCopy方法产生一个可变的副本,需要做两步: 

1. 让该类继承NSMutableCopying协议。 

2. 在类的代码实现部分重写继承自NSMutableCopying协议的mutableCopyWithZone:方法,该方法返回一个该类的可变对象副本。

2 深复制和浅复制

代码示例如下: 

GKHUser.h

[code]#import <Foundation/Foundation.h>

//继承NSCopying和NSMutableCopying协议
@interface GKHUser : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, strong) NSMutableString *name;//类似retain类型,引用计数
@property (nonatomic, assign) int age;
@end


GKHUser.m

[code]#import "GKHUser.h"

@implementation GKHUser

//重写copyWithZone:方法
- (id) copyWithZone:(NSZone *)zone {
    NSLog(@"执行copyWithZone:方法");
    //使用zone参数创建一个GKHUser对象
    GKHUser *user = [[[self class] allocWithZone:zone] init];

    user.name = self.name;//这里只是浅复制
    user.age = self.age;
    return user;
}

//重写mutableCopyWithZone:方法
- (id) mutableCopyWithZone:(NSZone *)zone {
    NSLog(@"执行mutableCopyWithZone:方法");
    //使用zone参数创建一个GKHUser对象
    GKHUser *user = [[[self class] allocWithZone:zone] init];

    user.name = self.name;//这里只是浅复制
    user.age = self.age;
    return user;
}
@end


main.m

[code]#import <Foundation/Foundation.h>
#import "GKHUser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        GKHUser *user1 = [GKHUser new];
        user1.name = [NSMutableString stringWithString:@"李太"];
        user1.age = 10;

        GKHUser *user2 = [user1 copy];//复制不可变副本
        [user1.name appendString:@"白"];
        user2.age = 20;

        NSLog(@"user1的名字为:%@", user1.name);
        NSLog(@"user1的年龄为:%d", user1.age);
        NSLog(@"user2的名字为:%@", user2.name);
        NSLog(@"user2的年龄为:%d", user2.age);

        GKHUser *user3 = [GKHUser new];
        user3.name = [NSMutableString stringWithString:@"张大"];
        user3.age = 30;

        GKHUser *user4 = [user3 mutableCopy];//复制可变副本
        [user3.name appendString:@"彪"];
        user4.age = 40;

        NSLog(@"user3的名字为:%@", user3.name);
        NSLog(@"user3的年龄为:%d", user3.age);
        NSLog(@"user4的名字为:%@", user4.name);
        NSLog(@"user4的年龄为:%d", user4.age);

    }
    return 0;
}


运行main.m结果:

执行copyWithZone:方法 

user1的名字为:李太白 

user1的年龄为:10 

user2的名字为:李太白 

user2的年龄为:20 

执行mutableCopyWithZone:方法 

user3的名字为:张大彪 

user3的年龄为:30 

user4的名字为:张大彪 

user4的年龄为:40

我们从结果中发现,不管是可变复制还是不可变复制,最后修改副本的name属性时候,会导致原始对象的属性也发生修改,为什么呢?因为这里的copyWithZone:和mutableWithZone:方法中对name属性的赋值是浅复制,如下图:



为了达到我们的效果,GKHUser.h和main.m代码不变,而GKHUser.m修改如下: 

GKHUser.m

[code]#import "GKHUser.h"

@implementation GKHUser

//重写copyWithZone:方法
- (id) copyWithZone:(NSZone *)zone {
    NSLog(@"执行copyWithZone:方法");
    //使用zone参数创建一个GKHUser对象
    GKHUser *user = [[[self class] allocWithZone:zone] init];

    user.name = [self.name copy];//这里对name指向的对象执行深复制,复制的副本为不可变的字符串
    user.age = self.age;
    return user;
}

//重写mutableCopyWithZone:方法
- (id) mutableCopyWithZone:(NSZone *)zone {
    NSLog(@"执行mutableCopyWithZone:方法");
    //使用zone参数创建一个GKHUser对象
    GKHUser *user = [[[self class] allocWithZone:zone] init];

    user.name = [self.name mutableCopy];//这里对name指向的对象执行深复制,复制的副本为可变的字符串
    user.age = self.age;
    return user;
}
@end


运行main.m结果:

执行copyWithZone:方法 

user1的名字为:李太白 

user1的年龄为:10 

user2的名字为:李太 

user2的年龄为:20 

执行mutableCopyWithZone:方法 

user3的名字为:张大彪 

user3的年龄为:30 

user4的名字为:张大 

user4的年龄为:40

这样就达到了我们的目的,user2是user1的不可变副本,实现了copy方法,而user4是user3的可变副本,实现了mutableCopy方法。 

其在内存中的示意图如下:


### 3. Objective-C中深拷贝浅拷贝的示例说明 在Objective-C中,浅拷贝深拷贝的区别主要体现在对象属性的复制方式上,尤其是当对象包含嵌套对象时。浅拷贝仅复制对象的第一层数据,并对嵌套对象保持引用共享,而深拷贝则递归复制所有层级的数据,确保拷贝对象原对象完全独立。 #### 浅拷贝示例 使用`copy`方法实现浅拷贝,对于不可变对象,通常返回原对象的引用;对于可变对象,则创建一个新实例并复制其内容。 ```objective-c #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray *originalArray = [NSMutableArray arrayWithObjects:[[NSMutableString alloc] initWithString:@"A"], nil]; NSArray *shallowCopy = [originalArray copy]; // 修改原数组中字符串对象的内容 NSMutableString *str = originalArray[0]; [str appendString:@"_Modified"]; // 输出结果:原数组浅拷贝数组都会显示修改后的内容 NSLog(@"Original array: %@", originalArray); // 输出 "A_Modified" NSLog(@"Shallow copy array: %@", shallowCopy); // 输出 "A_Modified" } return 0; } ``` 该示例表明,浅拷贝仅复制了顶层数组的结构,而内部的字符串对象仍然共享同一个实例。因此,修改原数组中的字符串对象会影响到浅拷贝数组[^1]。 #### 深拷贝示例 Objective-C中没有内置的通用深拷贝方法,通常需要实现`NSCopying`协议并手动处理嵌套对象的复制。 ```objective-c @interface CustomObject : NSObject <NSCopying> @property (nonatomic, strong) NSString *name; @end @implementation CustomObject - (id)copyWithZone:(NSZone *)zone { CustomObject *copy = [[CustomObject allocWithZone:zone] init]; copy.name = [self.name copy]; // 深拷贝属性 return copy; } @end int main(int argc, const char * argv[]) { @autoreleasepool { CustomObject *original = [[CustomObject alloc] init]; original.name = @"Test"; CustomObject *deepCopy = [original copy]; // 调用 copyWithZone: original.name = @"Modified"; NSLog(@"Original name: %@", original.name); // 输出 "Modified" NSLog(@"Deep copy name: %@", deepCopy.name); // 输出 "Test" } return 0; } ``` 在上述代码中,通过实现`copyWithZone:`方法,可以递归地复制对象的所有属性,从而实现深拷贝。修改原对象的属性不会影响拷贝对象,说明两者完全独立[^1]。 ### 内存管理注意事项 Objective-C中进行深拷贝时,需要注意内存管理问题。如果对象中包含手动管理内存的对象(如使用`retain`/`release`),必须确保在拷贝时正确处理引用计数,以避免内存泄漏或重复释放。例如,在`copyWithZone:`方法中应使用`copy`或`retain`来正确管理嵌套对象的内存状态。 ### 适用场景总结 - **浅拷贝**适用于对象结构简单、不需要修改嵌套对象的场景,例如临时读取数据。 - **深拷贝**适用于需要完全独立副本的场景,例如对象状态保存、多线程操作等。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yusirxiaer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值