美文网首页
NSCopying协议

NSCopying协议

作者: 白色天空729 | 来源:发表于2019-10-10 10:47 被阅读0次

如何让自定义对象支持 copy 操作?是重写 copy 方法么?当然不是,而是需要让自定义类实现 NSCopying 协议,该协议只有一个方法:

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

以前开发程序时,会把内存分成不同的“区”,而对象会创建在某个区里。现在不用了,每个程序只有一个“默认区”(default zone),实现该方法时,不必担心其中的 zone 参数。例如,我们要让自定义的 ACLStudent 类支持拷贝功能,则可以像如下这样:

ACLStudent.h

@interface ACLStudent : NSObject <NSCopying>

@property (nonatomic, assign, readonly) NSInteger studentId;
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName;
@end
-----

ACLStudent.m

@implementation ACLStudent

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName {
    self = [super init];
    if (self) {
        _studentId = studentId;
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    
    return self;
}

- (instancetype)copyWithZone:(NSZone *)zone {
    ACLStudent *copy = [[[self class] allocWithZone:zone] initWithStudentId:_studentId firstName:_firstName lastName:_lastName];
    
    return copy;
}

+ (BOOL)accessInstanceVariablesDirectly {
    return YES;
}

@end

上例中 copyWithZone: 方法使用了全能初始化方法(designated initializer)来执行拷贝对象的所有初始化工作。这里需要注意,对于未能在全能初始化方法中设置好的变量,需要在 copyWithZone: 方法中做特殊处理。例如,如果在 ACLStudent 类中增加一个变量,来存储当前学生所选选修课的科目,完整代码变成如下:


ACLStudent.h

@class ACLCourse;
@interface ACLStudent : NSObject <NSCopying>

@property (nonatomic, assign, readonly) NSInteger studentId;
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName;

- (void)addElective:(ACLCourse *)course;

- (void)removeElective:(ACLCourse *)course;
@end
--------

@implementation ACLStudent {
    NSMutableSet *_electives;  /**< 选修科目 */
}

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName {
    self = [super init];
    if (self) {
        _studentId = studentId;
        _firstName = [firstName copy];
        _lastName = [lastName copy];
        
        _electives = [NSMutableSet new];
    }
    
    return self;
}

- (void)addElective:(ACLCourse *)course {
    [_electives addObject:course];
}

- (void)removeElective:(ACLCourse *)course {
    [_electives removeObject:course];
}

- (instancetype)copyWithZone:(NSZone *)zone {
    ACLStudent *copy = [[[self class] allocWithZone:zone] initWithStudentId:_studentId firstName:_firstName lastName:_lastName];
    copy->_electives = [[NSMutableSet alloc] initWithSet:_electives copyItems:YES];
    
    return copy;
}

+ (BOOL)accessInstanceVariablesDirectly {
    return NO;
}

@end

注意全能初始化方法和 copyWithZone: 方法实现的变化, 这里我们对 _electives 对象执行的是深拷贝。

假如我们想让自定义对象支持 mutableCopy 操作,那又应该怎么操作呢?这需要自定义对象遵循 NSMutableCopying 协议, 该协议也只有一个方法:

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

其与 copy 相似,也是使用默认的 zone 参数来调用 mutableCopyWithZone:

如果类分为可变版本和不可变版本,那么就应该实现 NSMutableCopying, 且在可变类中覆写 copyWithZone: 方法时,应该返回一份不可变的版本。无论当前实例是否可变,若需获取其可变版本的拷贝,均应调用 mutableCopy 方法,若需不可变的拷贝,则总应该调用 copy 方法。例如对于不可变的 NSArray 和可变的 NSMutableArray,以下关系总是成立:

- [NSMutableArray copy] => NSArray
- [NSArray mutableCopy] => NSMutableArray

如何对 NSArray 执行深拷贝呢?苹果官方文档 Copying Collections 提供了以下两种方法。

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];
这种方式执行拷贝时,someArray 中的可变对象是执行深拷贝,而对于不可变对象,仍然执行的是浅拷贝。

方法2:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

这种方式实现的是真正意义上的深拷贝,oldArray 中所有元素都是深拷贝。

那么 copymutableCopy 返回的对象是执行的深拷贝还是浅拷贝呢?这篇博客 [copy与mutableCopy][] 对系统对象执行 copymutableCopy 时到底执行的深拷贝还是浅拷贝进行了讨论。其中系统对象分为了两类:

系统的非容器类对象,如 NSStringNSMutableStringNSNumber 等。
系统的容器类对象,如NSArrayNSMutableArrayNSDictionaryNSMutableDictionary 等。
现把结论摘抄如下:

对于系统的非容器类对象,如果对一不可变对象(如NSString)复制,copy 是指针复制(浅拷贝)和 mutableCopy 就是对象复制(深拷贝); 如果是对可变对象(如 NSMutableString)复制,copymutableCopy 都是深拷贝,但是 copy 返回的对象是不可变的。
对于系统的容器类对象,对不可变对象(如 NSArray)进行复制,copy 是指针复制(浅拷贝), mutableCopy 是对象复制(深拷贝), 但是不管是 copy 还是 mutableCopy, 且不论容器内对象是可变还是不可变,返回的容器内对象都是指针复制(浅拷贝)。
对于系统的容器类对象,对可变对象(如 NSMutableArray)进行复制时,copymutableCopy 都是对象复制(深拷贝),但是不管是 copy 还是 mutableCopy,且不论容器内对象是可变还是不可变,返回的容器内对象都是指针复制(浅拷贝)。

原文链接:http://liumh.com/2015/12/12/ios-understand-copy/

相关文章

  • 让自己创建的类具备Copy功能

    对于对象具有拷贝功能,则需实现 NSCopying 协议。 声明该类遵从 NSCopying 协议 实现 NSCo...

  • NSCopying协议

    NSCopyingNSCopying是一个与对象拷贝有关的协议。如果想让一个类的对象支持拷贝,就需要让该类实现NS...

  • NSCopying协议

    先来看一段代码 在删除arr1第一个元素的同时,arr2的第一个元素也被删除了,这很好理解,因为两个对象都指向同一...

  • NSCopying协议

    NSCopying协议 如果想令自己的类支持拷贝操作,那就要实现NSCopying协议。 现有自定义类PassMo...

  • NSCopying协议

    如何让自定义对象支持 copy 操作?是重写 copy 方法么?当然不是,而是需要让自定义类实现 NSCopyin...

  • 22: 理解NSCopying协议

    1.NSCopying协议 若想令自定义对象具有拷贝功能,则需要实现NSCopying协议 实现copyWithZ...

  • (二十一)[OC高效系列]理解NSCopying协议

    1.NSCopying协议 若想令自己所写的对象具有拷贝功能,则需要实现NSCopying协议 实现copyWit...

  • 52个有效方法(22) - 理解NSCopying协议

    NSCopying协议 若想令自己所写的对象具有拷贝功能,则需要实现NSCopying协议。 方法中应该用全能初始...

  • 《Effective Objective-C 2.0 》 阅读笔

    第22条:理解NSCopying协议 1. NSCopying协议 如果想令自己的类支持拷贝操作,必须实现NSCo...

  • iOS NSCopying 与 NSMutableCopying

    NSCopying: 如果要调用一个对象的copy方法,这个对象必须遵循NSCopying的协议。这个协议中规定了...

网友评论

      本文标题:NSCopying协议

      本文链接:https://www.haomeiwen.com/subject/rzjopctx.html