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中對象拷貝的相關概念和知識點。咱們在操做對象的時候,有時候進行拷貝,還要仔細考慮一下是深拷貝仍是淺拷貝。

相關文章
相關標籤/搜索