Copy程序员
主要内容:less
1. copy的基本使用优化
♠ copy的效果:atom
对源对象进行copy,创建出新的副本,彼此修改互不干扰!spa
♠ OC中有两种copy方式设计
1> copy指针
若是对象有可变/不可变之分,copy只能copy出不可变版本,若是没有此区分,copy方法就是创建一个副本。code
2> mutableCopy对象
创建对象的可变副本(仅仅是当对象有可变和不可变版本时才须要是要本方法)blog
@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:方法。
4. copy修饰的结构体
问题:block类型的变量为何要用copy修饰?
由于在ARC下,编译器底层对block作了一些优化,能够防止出现内存泄露,若是使用了strong,至关于强引用了一个栈区变量,从内存管理的角度而言,程序员须要管理的仅仅是堆区,全部在对block类型变量进行复制时要使用copy,对值进行一次copy操做,将其copy到堆区。