Objective-C Copy语法

来源:互联网 发布:淘宝天猫超市有客服吗 编辑:程序博客网 时间:2024/06/04 18:59


在OC语法中,提供了Copy语法(Copy + MutableCopy)用于对象的拷贝。其中很容易混淆的是浅拷贝和深拷贝。

所谓浅拷贝,即是地址拷贝,并不产生新的对象,而是对原对象的引用计数值加1即调用所谓的retain。而深拷贝,即是对象拷贝,产生新的对象副本,计数器为1,调用copy或mutableCopy。

何时用copy, 何时用 mutableCopy?

1、不可变对象→可变对象的转换:

       

NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];NSMutableArray *str2 = [array1 mutableCopy];

2、可变对象→不可变对象的转换:

NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"aa",@"bb",@"cc",@"dd",nil];NSArray *array1=[array2 copy];

3、可变对象→可变对象的转换(不同指针变量指向不同的内存地址):

NSMutableArray *array1= [NSMutableArray arrayWithObjects:@"a",@"b", nil];NSMutableArray *str2=[array1 mutableCopy];

通过上边的两个例子,我们可以很方便的将一个对象在可变和不可变之间转换,并且这里不用考虑内存使用原则(即引用计数的问题)。这就是深拷贝的魅力所在。

4、同类型对象之间的导向保持(不同指针变量指向同一块内存地址):

  a.

NSMutableString *str1=[NSMutableString stringWithString:@"hello world"];NSMutableString *str2=[str1 retain];[str1 release];

  b.

NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",nil];NSArray *str2=[array1 Copy];[array1 release];


通俗的讲,就是甲在执行交通导航任务,但接到上级新的命令要执行新的任务,那么在甲执行新任务之前,需要有人替代甲继续执行交通导航任务。这时候就要用到浅拷贝了。

则简化为:

问:什么时候用到深浅拷贝?

答:深拷贝是在要将一个对象从可变(不可变)转为不可变(可变)或者将一个对象内容克隆一份时用到;浅拷贝是在要复制一个对象的指针时用到。


下面通过一个例子来分析一下这个比较容易乱的Copy


1. 对于NSString/NSMutableString; NSArray/NSMutableArray...这些OC提供的类对象:


NSString/NSMutableString为例:


对于copy,返回的一定是不可变类型;而mutableCopy,返回的一定是可变类型。


①对于 mutableCopy,一定是深拷贝。



//对于 mutableCopy,都是深拷贝:对象的拷贝,产生新的对象void strMutableCopy(){        NSString *str=[[NSString alloc]initWithFormat:@"abcd"];        //产生了一个新的对象 计数器为1 原对象的计数器不变    NSMutableString *str2=[str mutableCopy];        //输出二者的地址,二者的地址是不同的    NSLog(@"str --> %p",str);    NSLog(@"str2 --> %p",str2);}

②对于 copy

如果是 NSString ---> NSString;则是浅拷贝;如果是 NSMutableString --->NSString;则是深拷贝。

如果是 NSString NSMutableString ---> NSMutableString;则是深拷贝。

Note :只有一种情况下是发生浅拷贝:不可变对象复制到不可变对象。


//浅拷贝:指针拷贝 不会产生新的对象,原对象计数器加1void strCopy(){        NSString *str=[[NSString alloc]initWithFormat:@"abcd"];    //因为NSString对象本身就不可变,所以并没产生新的对象,而是返回对象本身,会做一次retain操作,所以原对象也会retain    NSString *str2=[str copy];        //输出二者的地址,二者的地址是相同的    NSLog(@"str --> %p",str);    NSLog(@"str2 --> %p",str2);}

除了以上这种情形外,其他都是深拷贝。


2. 对于自定义对象的Copy:该类必须实现NSCopying协议,重写 copyWithZone方法。

同理,对于自定义对象的mutableCopy:必须实现 NSMutableCopying协议,重写 mutableCopyWithZone方法。

NSCopying协议中,其实只有一个协议方法, 如下:

@protocol NSCopying- (id)copyWithZone:(NSZone *)zone;@end

NSMutableCopying协议:

@protocol NSMutableCopying- (id)mutableCopyWithZone:(NSZone *)zone;@end

下面给出一个例子:

////  Person.h//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import <Foundation/Foundation.h>@interface Person : NSObject<NSCopying>@property(nonatomic,copy) NSString *name;@property(nonatomic,assign) int age;-(id)initWithName:(NSString*)name andAge:(int)age;@end

////  Person.m//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import "Person.h"@implementation Person-(instancetype)initWithName:(NSString*)name andAge:(int)age{    if ( self = [super init] )    {        self.age = age;        self.name = name;    }    return self;}-(id)copyWithZone:(NSZone *)zone{    Person* person = [[[self class] allocWithZone:zone] initwithName:self.name andAge:self.age];    return person;}@end
testing...
////  main.m//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(int argc, const char * argv[]){    @autoreleasepool {                Person *per1 = [[Person alloc] initWithName:@"张三" andAge:12];        Person *per2 = [p1 copy];                //输出两个 Person 对象的地址,二者是不同的        NSLog(@"per1 --> %p",per1);        NSLog(@"per2 --> %p",per2);    }    return 0;}

问:如果加入对于某些自定义对象是不可变的,怎么办呢?

答:只需要直接返回self就行了,如下:

-(id)copyWithZone:(NSZone *)zone{    return self;}
这样,输出的两个对象的地址就是相同的了。


下面了解一下关于如果某一个自定义类继承了 这个Person类的情况。

如果某一个子类继承了实现了NSCopying协议的基类,那么该子类也是会自动继承这个协议的方法。但是需要自己重新实现。

例如:有一个Student子类继承了这个Person类:


////  Student.h//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import <Foundation/Foundation.h>#import "Person.h"@interface Student : Person@property(nonatomic,copy)NSString *school;-(id)initWithName:(NSString *)name andAge:(int)age andSchool:(NSString*)school;@end

////  Student.m//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import "Student.h"#import "Person.h"@implementation Student-(id)initWithName:(NSString *)name andAge:age andSchool:(NSString*)school{    self = [super initWithName:name andAge:name];    if ( self != nil )    {        self.school = school;    }    return self;}-(id)copyWithZone:(NSZone *)zone{    Student *student = [super copyWithZone:zone];    student.school = self.school;    return student;}@end

testing...

////  main.m//  test_1////  Created by lin on 14-8-26.//  Copyright (c) 2014年 linshaolie. All rights reserved.//#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *student1 = [[Student alloc] initWithName:@"小明" andAge:20];                Student *student2 = [student1 copy];        //打印不同地址        NSLog(@"student1 --> %p", student1);        NSLog(@"student2 --> %p", student2);    }        return 0;}

注意其中copyWithZone方法的实现。

最后,记录下另外一种实现深复制的方法:使用Foundation的归档技术。下面是代码:

        NSArray *dataArray1 = @[@"aaa", @"bbb", @"ccc"];        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dataArray1];        NSMutableArray *dataArray2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];        //    上述即实现了将dataArray1深复制给dataArray2,另外一种简便写法是:        NSLog(@"dataArray1 --> %p ", dataArray1);        NSLog(@"dataArray2 --> %p ", dataArray2);





0 0
原创粉丝点击