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

被折叠的 条评论
为什么被折叠?



