目录
前言
这篇文章主要是讲NSCopying的用法。
一、简介
在 Objective-C 中,NSCopying协议用于创建对象的副本。这对于需要复制对象而不是简单地引用它们的情况非常有用。实现NSCopying`协议的类必须提供一个方法,用于返回对象的副本。
我们可以看一下NSCopying的定义:
/*************** Basic protocols ***************/
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
官方的介绍也很简单,NSCopying是一个基础的协议,这个协议只有一个copyWithZone方法。
此方法用于创建并返回接收者的副本。zone参数允许指定新对象的内存区域,但通常可以忽略此参数,直接传递 `nil`。
二、实现 NSCopying 的步骤
实现NSCopying需要两个步骤:
- 声明 NSCopying 协议:在头文件中声明类遵守 NSCopying`协议。
- 实现 copyWithZone 方法:在实现文件中编写 copyWithZone方法,创建并返回对象的副本。
以下是一个实现 `NSCopying` 协议的示例:
@interface MyObject : NSObject<NSCopying>
@property (nonatomic,strong) NSString * name;
@property (nonatomic,assign) NSUInteger age;
@end
@implementation MyObject
- (id)copyWithZone:(NSZone *)zone {
MyObject *copy = [[[self class] allocWithZone:zone] init];
if (copy) {
copy->_name = [_name copyWithZone:zone];
copy->_age = _age;
}
return copy;
}
@end
使用NSCopying的实例代码如下:
MyObject *original = [[MyObject alloc] init];
original.name = @"Original";
original.age = 30;
MyObject *copy = [original copy];
NSLog(@"原始对象: %@, Age: %ld", original.name, (long)original.age);
NSLog(@"深拷贝对象: %@, Age: %ld", copy.name, (long)copy.age);
三、深拷贝与浅拷贝
浅拷贝:新对象的属性指向原对象的同一实例。通常通过直接赋值实现。
深拷贝:新对象的属性是原对象属性的副本。需要递归复制每个属性。
在上面的示例中,name 属性是通过copyWithZone:方法进行深拷贝,而 age属性则是简单的浅拷贝。
使用 NSCopying 的注意事项
1. 不可变对象:对于不可变对象,浅拷贝和深拷贝通常没有区别。
2. 可变对象:对于可变对象,必须深拷贝以确保新对象的独立性。
3. 自定义类:实现NSCopying协议时,需要确保所有属性都正确复制。
四、NSCopying类的使用场景
1.防止对象被篡改
还以上面的MyObject对象为例,假如我们的MyObject只有两个属性name和age两个属性,当我们进行赋值操作之后,修改新对象的值之后,可能会修改原来的想的值。
#import "ViewController.h"
@interface MyObject : NSObject
@property (nonatomic,strong) NSString * name;
@property (nonatomic,assign) NSUInteger age;
@end
@implementation MyObject
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyObject *original = [[MyObject alloc] init];
original.name = @"Original";
original.age = 30;
MyObject *copy =original;
//修改对象的属性
copy.name = @"Modified";
copy.age = 20;
NSLog(@"原始对象: %@, Age: %ld", original.name, (long)original.age);
NSLog(@"新对象: %@, Age: %ld", copy.name, (long)copy.age);
}
@end
上述代码运行之后, 控制台打印信息如下:
在上述的代码中,我们修改copy对象的name和age之后,原始对象的属性也被修改。
当我们的MyObject对象实现了NSCopying协议之后,对象的拷贝就变成了深拷贝,这个时候再次修改对象的值可以防止原来对象的值被修改。
代码如下:
#import "ViewController.h"
@interface MyObject : NSObject
@property (nonatomic,strong) NSString * name;
@property (nonatomic,assign) NSUInteger age;
@end
@implementation MyObject
- (id)copyWithZone:(NSZone *)zone {
MyObject *copy = [[[self class] allocWithZone:zone] init];
if (copy) {
copy->_name = [_name copyWithZone:zone];
copy->_age = _age;
}
return copy;
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyObject *original = [[MyObject alloc] init];
original.name = @"Original";
original.age = 30;
MyObject *copy = [original copy];
//修改对象的属性
copy.name = @"Modified";
copy.age = 20;
NSLog(@"原始对象: %@, Age: %ld", original.name, (long)original.age);
NSLog(@"深拷贝对象: %@, Age: %ld", copy.name, (long)copy.age);
}
@end
代码运行之后,控制台打印信息如下:
我们发现即使我们修改了copy对象的name属性,也不会影响原来的对象的值被修改。
2.多线程编程
- (void)method2{
//MyObject实现了NSCopying协议
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_CONCURRENT);
MyObject *sharedObject = [[MyObject alloc] init];
sharedObject.name = @"Shared";
//在每个线程中使用拷贝的对象,保证线程的安全
dispatch_async(queue, ^{
MyObject *threadCopy = [sharedObject copy];
threadCopy.name = @"线程1";
NSLog(@"线程1: %@", threadCopy.name);
});
dispatch_async(queue, ^{
MyObject *threadCopy = [sharedObject copy];
threadCopy.name = @"线程2";
NSLog(@"线程2: %@", threadCopy.name);
});
}
3.保持对象的状态
假如我们正在修改一个对象的时候,发生了异常情况需要恢复到修改之前的状态,我们就可以通过下面的代码实现。
- (void)keepThreadState{
// 假设MyObject实现了 NSCopying
MyObject *original = [[MyObject alloc] init];
original.name = @"Original";
original.age = 30;
// 修改之前保存对象的状态
MyObject *savedState = [original copy];
original.name = @"Modified";
NSLog(@"修改: %@", original.name); // Output: Modified
// 必要的时候恢复savedState
original = savedState;
NSLog(@"恢复: %@", original.name); // Output: Original
}