OC学习篇之---对象的拷贝 分类: IOS 2014-12-14 18:02 2128人阅读 评论(0) 收藏

在前一篇文章中咱们说到了如何解决对象的循环引用问题: http://blog.csdn.net/jiangwei0910410003/article/details/41926369 ,这一篇文章咱们就来介绍一下OC中的对象拷贝概念,这个对于面向对象语言中都会有这种的问题,只是不一样的语言有不一样的解决方式:C++中有拷贝构造函数,Java中须要实现Cloneable接口,在clone方法中进行操做。可是不过OC更偏向于Java这种方式,OC中若是一个对象须要被拷贝,他须要实现协议:

<NSCopying><NSMutableCopying>数组

从名字上咱们能够看到,一个协议是用于不可变对象的,一个协议适用于可变对象的app


首先来介绍一下对象的拷贝的概念吧:框架

为何要由对象的拷贝这么一个概念呢?看一个场景:假如如今一个对象中又一个数组对象,如今咱们生成一个对象,同时将这个对象赋值给另一个对象,那么如今问题是这两个对象中的数组对象是同一个,那么若是一个对象中去修改这个数值中的内容,另一个对象中的数组内容也会被修改,至关于这个数组对象是共享的,固然咱们有时候是不但愿这种形式的出现的,这时候咱们就出现了对象的拷贝。iphone

具体来看一个例子吧函数


1、系统类对象的拷贝测试

//
//  main.m
//  30_CopyObject
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 
 */
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //对象具有拷贝功能,必须实现以下协议
        //<NSCopying>、<NSMutableCopying>
        
        //copy方法返回的是一个不可变对象,mutableCopy方法返回的是一个可变对象
        
        /*
        NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
        NSMutableArray *array2 = [array1 retain];
        //retain只是引用计数+1,没有建立新的对象
        //array1与array2指针相同,指向同一个对象
        if(array1 == array2){
            NSLog(@"array1 == array2");
            NSLog(@"array1的引用计数:%ld",array1.retainCount);
        }
         */
        
        NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil];
        //复制对象,建立一个新的副本对象
        //这里使用copy方法复制,返回的是一个不可变数组,可是用一个可变数组来声明,可是咱们关心的是指针的的内容,而不是类型
        //因此array2的真实类型仍是不可变类型的
        NSMutableArray *array2 = [array1 copy];//array2计数为:1,由于是新建立出来的对象
        
        //使用mutableCopy方法,返回的就是可变数组
        //固然这种方法只针对于那些有可变对象之分有用,对于其余的对象这个方法和copy方法的效果是同样的
        NSMutableArray *array3 = [array1 mutableCopy];
        
        if(array1 != array2){
            NSLog(@"array1 != array2");
            NSLog(@"array1的引用计数:%ld",array1.retainCount);
            NSLog(@"array2的引用计数:%ld",array2.retainCount);
        }
        [array2 release];
        [array1 release];
        
        
    }
    return 0;
}
咱们看到,NSMutableArray有一个mutableCopy方法,这样返回的一个数组对象就是一个拷贝对象了。

可是这里须要注意的是:优化

copy方法和mutableCopy方法的区别atom

这两个方法的区别只在于那些有可变对象和不可变对象之分的对象上,对于没有这种区分的对象来讲,这两个方法的效果是同样的。spa

[不可变对象 copy]是假拷贝,等价于[不可变对象 retain].net

[不可变对象 mutableCopy是真拷贝



2、深拷贝和浅拷贝

在拷贝对象中也是有深拷贝和浅拷贝之分的

浅拷贝:只拷贝全部属性对象的指针

深拷贝:拷贝属性对象的内容

看个例子:

Person.h

//
//  Person.h
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCopying>

@property(nonatomic,retain)NSMutableArray *apples;
@property(nonatomic)int age;

@end


Person.m

//
//  Person.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import "Person.h"

@implementation Person

- (id)copyWithZone:(NSZone *)zone{
    //建立一个新的副本对象
    //这个方法是会被继承的,因此这里仍是不用
    //[Person allocWithZone:<#(struct _NSZone *)#>];
    Person * p = [[self class] allocWithZone:zone];
    //p.apples = _apples;//是指针赋值,因此仍是浅拷贝
    //深拷贝
    //拷贝以后引用计数会+1,须要release如下
    p.apples = [_apples mutableCopy];
    p.age = _age;
    
    [p.apples release];
    
    //可是若是咱们使用->语法就不须要了,由于咱们没有使用set方法,引用计数没有操做
    //可是这种方式咱们不采用
    //p->_apples = [_apples mutableCopy];
    
    return p;
}

@end

咱们看到,Person实现了NSCopying协议,而后须要实现一个方法:copyWithZone

在这个方法中咱们开始进行拷贝操做:

Person类中有一个属性类型是数组

这里咱们须要生成一个Person对象,而后进行属性的拷贝,最后在返回这个对象

浅拷贝:直接复制数组指针

深拷贝:直接复制数组的内容,这里能够直接使用mutableCopy方法进行实现


测试类

main.m

//
//  main.m
//  31_DeepCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"

//深拷贝和浅拷贝
//默认是浅拷贝
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:2];
        
        for(int i=0;i<2;i++){
            Person *p = [[Person alloc] init];
            [array1 addObject:p];
            [p release];
        }
        
        //引用计数都是1
        for(Person *p in array1){
            NSLog(@"复制以前的引用计数:%ld",p.retainCount);
            NSLog(@"复制以前的指针:%p",p);
        }
        
        //引用计数都是2,由于是浅拷贝,又有指针指向对象了,array2也是使用了person
        //浅拷贝:只拷贝对象指针
        //深拷贝:复制属性
        NSArray *array2 = [array1 copy];
        for(Person *p in array2){
            NSLog(@"复制以前的引用计数:%ld",p.retainCount);
            NSLog(@"复制以前的指针:%p",p);
        }
        
        //这里Person中有一个属性是NSMutableArray,可是咱们只是赋值,并非拷贝
        //因此这里还不算是深拷贝
        Person *p = [[Person alloc] init];
        p.apples = [NSMutableArray arrayWithObjects:@"iphone",@"ipad", nil];
        p.age = 20;
        
        Person *p1 = [p copy];
        
        if(p != p1){
            NSLog(@"p1.age=%d",p1.age);
            NSLog(@"p1.apples=%@",p1.apples);
        }
        
    }
    return 0;
}


3、字符串的拷贝

//
//  main.m
//  32_NSStringCopy
//
//  Created by jiangwei on 14-10-13.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "Person.h"

//字符串为何使用copy
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        NSMutableString *name = [NSMutableString stringWithString:@"jack"];
        p.name = name;
        
        //人的名字被修改了
        //若是Person的name是retain,则此处的name和person对象的name执行的是同一个字符串对象
        //此处的name修改以后,会致使person的name也被修改,破坏了person对象的封装性
        //正常状况下,咱们会使用set方法设置名字
        //因此若是使用的是copy的话,就不会修更名字了
        [name appendString:@"-tom"];
        
        //Foundation框架中可复制的对象,当咱们拷贝的是一个不可变对象时候
        //他的做用至关于retain(系统作的内存优化)
        
        //因此这里的若是换成NSString类型的时候,其实没有拷贝的动做的,由于NSString是不可变的
        //可是使用mutableCopy就能够作到拷贝了,mutableCopy是真正意义上的拷贝
        //mutableCopy拷贝方法,无论什么对象都是真实拷贝
        
        //[不可变对象 copy]是假拷贝,等价于[不可变对象 retain]
        //[不可变对象 mutableCopy是真拷贝
    }
    return 0;
}
这里为何要单独说一下字符串的拷贝呢?

由于字符串是一个特殊的对象,咱们应该调用他的copy方法。由于咱们对于字符串其实咱们是指望他只有一分值得,就看上面的例子:

咱们用NSMutableString产生一个name,而后将其赋值给person对象,当咱们在外面修改name的内容的时候,其实person的name属性的值也应该修改。因此咱们通常在拷贝字符串对象的时候,都会调用他的copy方法


总结

这一篇文章主要介绍了OC中对象拷贝的相关概念和知识点。咱们在操做对象的时候,有时候进行拷贝,还要仔细考虑一下是深拷贝仍是浅拷贝。

相关文章
相关标签/搜索