Copy
主要内容:
- copy的基本使用
- 自定义对象的copy属性
- 支持copy的自定义对象
1. copy的基本使用
♠ copy的效果:
对源对象进行copy,建立出新的副本,彼此修改互不干扰!
♠ OC中有两种copy方式
1> copy
如果对象有可变/不可变之分,copy只能copy出不可变版本,如果没有此区分,copy方法就是建立一个副本。
2> mutableCopy
建立对象的可变副本(仅仅是当对象有可变和不可变版本时才需要是要本方法)
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self copyDemo1]; NSLog(@"------------------"); [self copyDemo2]; } - (void)copyDemo1{ NSString *str1 = @"copy1"; NSLog(@"%@ %p",[str1 class],str1); // copy => 不可变 不产生新对象 相当于引用计数器+1 id obj = [str1 copy]; NSLog(@"%@ %p",[obj class],obj); // mutableCopy => 可变 产生新对象 id obj2 = [str1 mutableCopy]; NSLog(@"%@ %p",[obj2 class],obj2); } - (void)copyDemo2{ NSMutableString *str2 = [NSMutableString stringWithString:@"copy2"]; NSLog(@"%@ %p",[str2 class],str2); // copy => 不可变 产生新对象 id obj1 = [str2 copy]; NSLog(@"%@ %p",[obj1 class],obj1); // mutableCopy => 可变,产生新对象 id obj2 = [str2 mutableCopy]; NSLog(@"%@ %p",[obj2 class],obj2); } @end
♠ 小结:
可变 ---> 不可变
可变 ---> 可变
不可变 ---> 可变 以上3种都会产生新的对象(深拷贝)
不可变 ---> 不可变 不会产生新对象,仅仅是对计数器+1(浅拷贝)
2. 自定义对象的copy属性
♠ 当一个对象的NSString类型属性使用strong 修饰
@property (nonatomic,strong) NSString *name;
如果对该属性做如下设置
person *p = [[person alloc]init]; NSMutableString *str = [NSMutableString stringWithString:@"aaaa"]; p.name = str; NSLog(@"%@",[p.name class]); // 输出结果:__NSCFString [str setString:@"bbbb"]; NSLog(@"%@",[p.name class]); // 输出结果:__NSCFString
从打印输出可以发现:
p.name属性的类型已经变为NSMutableString类型,而与我们当初定义的类型不一致,为什么?
因为一个对象的准确类型是在给改对象“分配内存空间”的时候制定的类型,而程序员指定属性为某对象的类型之后就可以具有该对象的方法,而能否运行成功取决于该属性的实际类型,如果使用了实际类型不存在的方法,将会报"unrecognized selector send to instance"
例如:
id aa = [[NSObject alloc]init]; NSString *string = aa; NSLog(@"%zd",string.length);
此代码块中,定义了NSString类型的变量,试图指向了NSObject的对象,当调用NSString的length方法时,编译可以通过,但是由于string的实际类型是NSObject类型,并不存在该方法,所有会报方法不存在错误:
unrecognized selector sent to instance 0x7f9628c8b7f0‘
发现问题:
当我们使用strong修饰成员属性时,将会有多个指针指向相同的对象,并可对其进行操作,而在程序设计过程中,有些时候我们的操作并不希望影响源数据,此时可能就需要改变变量的修饰为copy
♠ 面向对象程序开发中,有一个非常重要的原则
开闭原则:
- 开:对内开放,想怎么改,就怎么改
- 闭:对外封闭,只能用,不能改
定义成copy的属性,在设置时会默认进行一次copy操作;
-> 对可变属性进行copy ———— 新建副本
-> 对不可变属性进行copy ———— 不会创建新的对象,只是计数器+1,跟strong类型一致的。
使用注意:
对于有可变与不可变之分类型的属性而言,由于copy操作得到的将是不可变类型,所有对于可变类型的属性,不应该使用copy去修饰。
3. 支持copy的自定义对象
先来看程序,有自定义类,有两个成员属性
@property (nonatomic,copy)NSString *name; @property (nonatomic,assign) int age;
控制器中实例化对象,并尝试使用copy
person *p1 = [[person alloc] init]; p1.name = @"aaa"; p1.age = 12; person *p2 = [p1 copy]; p2.name = @"bbb"; p2.age = 14;
运行可发现:
-[person copyWithZone:] 系统将提示没有实现copyWithZone方法;
那么问题来了,copyWithZone:与copy方法有什么关系?
苹果官方文档中描述:
Returns the object returned by copyWithZone:, This is a convenience method for classes that adopt the NSCopyingprotocol. An exception is raised if there is no implementation for copyWithZone:. NSObject does not itself support the NSCopyingprotocol. Subclasses must support the protocol and implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super first, to incorporate its implementation, unless the subclass descends directly from NSObject.
我们可以从中得到一些信息:
1> copy 是 copyWithZone的简化形式,如果没有实现copyWithZone方法而调用copy会出现异常;
2> NSObject 类本身并没有遵守NSCopying协议,子类想要使用copy方法,必须遵守协议,并且实现copyWithZone方法;
3> 子类实现copyWithZone:方法必须先调用父类的copyWithZone,除非子类直接继承于NSObject
也就是说若要是的我们自定义类能够拷贝,有两个条件:
* 类遵守NSCopying协议
* 类实现copyWithZone:方法
Zone: 分配对象是需要内存空间的,如果指定了zone,就可以指定新对象的内存空间,但是zone是一个非常古老的技术,为避免在堆中出现碎片而使用的,如今已几乎不用。
- (id)copyWithZone:(NSZone *)zone{ person *p = [[self.class alloc]init]; p.name = self.name; p.age = self.age; return p; }
self.class 的原因:
1> copyWithZone: 是一个对象方法,self.class 获得类对象
2> 保证创建的对象都是person类或者子类对象
如果父类也实现了copyWithZone:方法,必须调用父类copyWithZone:方法。
IOS — Copy