1. 程式人生 > >ios學習路線—Objective-C(深淺拷貝)

ios學習路線—Objective-C(深淺拷貝)

在ObjC中,什麼是深淺拷貝?  深淺拷貝分別指深拷貝和淺拷貝,即 mutableCopy 和 copy 方法。  copy複製一個不可變物件,而 mutableCopy 複製一個 mutable 可變物件。

 

非容器類物件  如NSString,NSNumber等一類物件 
示例1:

// 非容器類物件
    NSString *str = @"origin string";
    NSString *strCopy = [str copy];
    NSMutableString *mstrCopy = [str mutableCopy];
    [mstrCopy appendString:
@"??"]; // NSLog(@"array1 = %p", array1); // NSLog(@"arrayCopy1 = %p", arrayCopy1);

檢視記憶體可以發現,str和strCopy指向的是同一塊記憶體區域,我們稱之為弱引用(weak reference)。而mstrCopy是真正的複製,系統為其分配了新記憶體空間,儲存從str複製過來的字串值。從最後一行程式碼中修改這些值而不影 響str和strCopy中可證明。

示例2:

    NSMutableString *mstr = [NSMutableString stringWithString:@"
origin"]; NSString *strCopy = [mstr copy]; NSMutableString *mstrCopy = [mstr copy]; NSMutableString *mstrMCopy = [mstr mutableCopy]; //[mstrCopy appendString:@"1111"]; //error [mstr appendString:@"222"]; [mstrMCopy appendString:@"333"];

以上四個物件所分配的記憶體都是不一樣的。而且對於mstrCopy,它所指向的其實是一個imutable物件,是不可改變的,所以會出錯。這點要注意,好好理解。

小結: 
1.如果對一個不可變物件複製,copy是指標複製,即淺拷貝;而mutableCopy則是物件複製,即深拷貝。 
2.如果是對可變物件複製,都是深拷貝,但是copy複製返回的物件是不可變的。

容器類物件深淺複製 
比如NSArray,NSDictionary等。對於容器類本身,上面討論的結論也適用的,下面探討的是複製後容器內物件的變化。

示例3:

    /* copy返回不可變物件,mutablecopy返回可變物件 */
    NSArray *array1     = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
    NSArray *arrayCopy1 = [array1 copy];
    //arrayCopy1是和array同一個NSArray物件(指向相同的物件),包括array裡面的元素也是指向相同的指標
    NSLog(@"array1 retain count: %d",[array1 retainCount]);      // 2
    NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);    //  2

    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
    //mArrayCopy1是array1的可變副本,指向的物件和array1不同,但是其中的元素和array1中的元素指向的還是同一個物件。mArrayCopy1還可以修改自己的物件
    [mArrayCopy1 addObject:@"de"];
    [mArrayCopy1 removeObjectAtIndex:0];

array1和arrayCopy1是指標複製,而mArrayCopy1是物件複製,符合前面示例1討論的結論。mArrayCopy1可以改變其內的元素:刪除或新增。但容器內的元素內容都是淺拷貝。

 

示例4

    NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    NSArray *mArrayCopy2 = [mArray1 copy];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    // mArray1和mArrayCopy2指向同一物件,retain值+1。

    NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy];
    NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]);
    //mArrayCopy2和mArray1指向的是不一樣的物件,但是其中的元素都是一樣的物件——同一個指標

    NSMutableString *testString = [mArray1 objectAtIndex:0];
    //testString = @"1a1";//這樣會改變testString的指標,其實是將@“1a1”臨時物件賦給了testString
    [testString appendString:@" tail"];//這樣以上三個陣列的首元素都被改變了

由此可見,對於容器而言,其元素物件始終是指標複製。如果需要元素物件也是物件複製,就需要實現深拷貝。

 

示例5

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"] ,[NSString stringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSLog(@"array[2] = %@, deepCopyArray[2]=%@ ",[array objectAtIndex:2], [deepCopyArray objectAtIndex:2]);  //輸出值是一樣的
NSLog(@"array[2]         %p", [array objectAtIndex:2]);
NSLog(@"deepCopyArray[2] %p", [deepCopyArray objectAtIndex:2]);   
  //最後兩個列印的log記憶體地址值是一樣的


NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray 是完全意義上的深拷貝,而deepCopyArray則不是,對於 deepCopyArray 內的不可變元素其還是指標複製。

 

自己實現深拷貝的方法 NSDictionaryMutableDeepCopy.h

#import <foundation /Foundation.h>
@interface NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy;
@end

NSDictionaryMutableDeepCopy.m

#import "NSDictionaryMutableDeepCopy.h"
@implementation NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy {
    NSMutableDictionary *ret = [[NSMutableDictionary alloc]
                                initWithCapacity:[self count]];
    NSArray *keys = [self allKeys];
    for (id key in keys) {
        id oneValue = [self valueForKey:key];
        id oneCopy = nil;
        if ([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
            oneCopy = [oneValue mutableDeepCopy];
        }
        else if ([oneValue respondsToSelector:@selector(mutableCopy)]) {
            oneCopy = [oneValue mutableCopy];
        }
        if (oneCopy == nil) {
            oneCopy = [oneValue copy];
        }
        [ret setValue:oneCopy forKey:key];
    }
    return ret;
}

使用類別方法來實現 
如果是我們定義的物件,那麼我們自己要實現NSCopying,NSMutableCopying這樣就能呼叫copy和mutablecopy了。舉個例子

@interface MyObj : NSObject<nscopying ,NSMutableCopying>
{
         NSMutableString *name;
         NSString *imutableStr;
         int age;
}
@property (nonatomic, retain) NSMutableString *name;
@property (nonatomic, retain) NSString *imutableStr;
@property (nonatomic) int age;

@end

@implementation MyObj
@synthesize name;
@synthesize age;
@synthesize imutableStr;
- (id)init
{
         if (self = [super init])
         {
                   self.name = [[NSMutableString alloc]init];
                   self.imutableStr = [[NSString alloc]init];
                   age = -1;
         }
         return self;
}

- (void)dealloc
{
         [name release];
         [imutableStr release];
         [super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
         MyObj *copy = [[[self class] allocWithZone:zone] init];
         copy->name = [name copy];
         copy->imutableStr = [imutableStr copy];
//       copy->name = [name copyWithZone:zone];;
//       copy->imutableStr = [name copyWithZone:zone];//
         copy->age = age;
         return copy;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
    MyObj *copy = NSCopyObject(self, 0, zone);
    copy->name = [self.name mutableCopy];
    copy->age = age;
    return copy;
}
@end