IOS — Copy

Copy程序员

主要内容:less

  • copy的基本使用
  • 自定义对象的copy属性
  • 支持copy的自定义对象

 

 

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到堆区。

相关文章
相关标签/搜索