[Foundation]框架--对象复制

本文介绍了Objective-C中Object类的copy和mutableCopy方法,用于创建对象的副本。copy方法返回不可变副本,而mutableCopy生成可变副本。通过示例代码展示了如何修改副本以及浅复制和深复制的区别。深复制确保两个对象间无共享部分,而浅复制可能导致对象间的属性共享并互相影响。文章还提到了setter方法中的复制选项,以及使用copy指示符的影响。

Object类提供了copy和mutableCopy方法,通过这两个方法即可复制已有对象的副本。
1. copy和mutableCopy方法
1.copy方法用于复制对象副本,返回对象不可修改的副本,例如程序调用NSMulateString的copy方法,将返回不可修改的字符串对象。
2. mutableCopy方法用于复制对象可变副本,即使被复制的对象本身不可改变,程序调用NSMulateString的mutableCopy方法,将返回可修改的字符串对象。
程序代码如下:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString* book = [NSMutableString stringWithString:@"疯狂iOS讲义"];
        //复制book字符串的可变副本
        NSMutableString* bookcopy = [book mutableCopy];
        //修改副本,对原字符串没有任何影响
        [bookcopy replaceCharactersInRange:NSMakeRange(2, 3) withString:@"Android"];
        //此处看到原字符串的值
        NSLog(@"book的值为%@", book);
        //字符串副本的值
        NSLog(@"bookcopy的值为%@", bookcopy);
        NSString* str = @"fkit";
        NSMutableString* strcopy = [str mutableCopy];
        //向后追加字符串
        [strcopy appendString:@".org"];
        NSLog(@"%@", strcopy);
    }
    return 0;
}

代码运行结果如下:
在这里插入图片描述
对于copy方法,如果要强行改变复制对象,则会出现错误。
具体代码如下:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString* str = @"FPS";
        NSMutableString* strcopy = [str copy];
        [strcopy appendString:@"aaa"];
    }
    return 0;
}

运行结果如下:
在这里插入图片描述
2. NSCopying和NSMutableCopy协议
使用copy和mutableCopy复制对象方便,但是自己定义的类不能调用该方法。
如果调用,一般在编译的时候不会出错,在运行的时候会出错。
在这里插入图片描述
在这里插入图片描述
进行上述操作后的代码:
接口部分:

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FKDog : NSObject
@property (nonatomic, strong) NSMutableString* name;
@property (nonatomic, assign) int age;
@end
NS_ASSUME_NONNULL_END

实现部分:

#import "FKDog.h"
@implementation FKDog
@synthesize name;
@synthesize age;
 1. (id) copyWithZone: (NSZone* ) zone {
    NSLog(@"---执行copyWithZone---");
    FKDog* dog = [[[self class] allocWithZone: zone] init];
    //使用zone参数创建FKDog
    dog.name = self.name;
    dog.age = self.age;
    return dog;
}
@end

主函数部分:

#import <Foundation/Foundation.h>
#import "FKDog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog* dog1 = [FKDog new];
        dog1.name = [NSMutableString stringWithString:@"lai"];
        dog1.age = 19;
        NSLog(@"dog1的名字为:%@", dog1.name);
        NSLog(@"dog1的年龄为:%d ", dog1.age);
        FKDog* dog2 = [dog1 copy];
        NSLog(@"dog2的名字为:%@", dog2.name);
        NSLog(@"dog2的年龄为:%d", dog2.age);
        dog2.name = [NSMutableString stringWithString:@"li"];
        dog2.age = 18;
        NSLog(@"dog2的修改后名字为:%@", dog2.name);
        NSLog(@"dog1的修改后年龄为:%d", dog2.age);
    }
    return 0;
}

代码运行结果:
在这里插入图片描述
copy方法的返回对象是不能改变的,因为我们没有此处的FKDog类没有提供不可变类,如果FKDog类是不可变类,则copyWithZone:返回不可变的FKDog对象。
3. 浅复制和深复制
1>浅复制
为了更好理解 浅复制和深复制,先看代码:

#import <Foundation/Foundation.h>
#import "FKDog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog* dog1 = [FKDog new];
        dog1.name = [NSMutableString stringWithString: @"lai"];
        dog1.age = 20;
        FKDog* dog2 = [dog1 copy];
        [dog2.name replaceCharactersInRange:NSMakeRange(0, 3) withString:@ "ming"];
        NSLog(@"dog1的name为%@", dog1.name);
        NSLog(@"dog2的name为%@", dog2.name);
    }
    return 0;
}

代码运行结果如下:
在这里插入图片描述
为了解释这个问题,参考下图,程序建立了一个FKDog对象,并使用指针指向对象后的内存存储示意。
在这里插入图片描述

	dog.name = self.name;
    dog.age = self.age;

dog代表复制出来的对象,程序将被复制对象的name复制给dog的name,此时name是一个指针变量,该变量存放的是字符串的地址,相当于dog和被复制对象同时指向同一个字符串。如下图:
在这里插入图片描述
此时dog1和dog2虽然指向不同的FKDog对象,但是两个FKDog对象的实例变量name都指向的是同一个NSMutableString对象,这样不论你修改任何一个FKDog的name属性值,另一个也会随之改变。
2>深复制
深复制采取与此不同的方式,深复制不仅会复制对象,而且会一直“递归”,复制每一个指针类型的实例变量,知道两个对象没有任何公用的部分。
在上面代码实现部分实现深复制:

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

总结:
当实现深复制时,可以保证两个对象之间没有任何公用的部分,当一个变量改变时,另一个变量不受影响。

主函数部分:

#import <Foundation/Foundation.h>
#import "FKDog.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKDog* dog1 = [FKDog new];
        dog1.name = [NSMutableString stringWithString: @"lai"];
        dog1.age = 19;
        FKDog* dog2 = [dog1 copy];
        [dog2.name replaceCharactersInRange:NSMakeRange(0, 3) withString:@ "ming"];
        NSLog(@"dog1的name为%@", dog1.name);
        NSLog(@"dog2的name为%@", dog2.name);
    }
    return 0;
}

代码运行结果如下:
在这里插入图片描述
4. setter方法的复制选项
前面提到的setter和getter方法时提到可以使用copy指示符,意思就是程序调用setter方法复制时,实际上是将传入参数的副本赋给程序的实例变量。
接口部分如下:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKItem : NSObject
@property (nonatomic, copy) NSMutableString* name;
@end

NS_ASSUME_NONNULL_END

主函数部分:

#import <Foundation/Foundation.h>
#import "FKItem.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKItem* item = [FKItem new];
        item.name = [NSMutableString stringWithString:@"疯狂iOS"];
        [item.name appendString:@"fkit"];
    }
    return 0;
}

代码运行时候就会出错:
在这里插入图片描述
实际上我们的setName:方法的代码如下:
在这里插入图片描述
因为我们的copy默认复制不可变副本,所以即使程序传入了NSMutableString,但是因为我们使用点语法时得到的是不可变的副本,所以对象的name的实例变量依然是不可变字符串,所以不可修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值