ObjectiveC中的賦值,對象拷貝,淺拷貝與深拷貝

在開發過程中我們經常會遇到對象拷貝的問題,下面我們分別討論賦值操作、對象拷貝、以及淺拷貝(Shallow copy)與深拷貝(Deep copy)的區別與各自的實現方式。


一、不同對象的賦值操作

Objective-C中有兩類對象,一類是結構體(或者基本數據類型也算),另一類是NSObject對象。

對於結構體,代碼直接會操作其實體,因此賦值操作會創建一個源對象的副本(一個新的對象);而對於NSObject對象,必須使用指針來操作對象,所以其賦值操作相當於複製了指針,而非對象,也就是說賦值操作使得源指針和新指針都指向同一個NSObject對象。這樣講有些難以理解,請看下面的代碼:

// main.m

#import <Foundation/Foundation.h>

@interface TestObject : NSObject
{
    @public
    int x;
    int y;
}
@end

@implementation TestObject
@end

typedef struct TestStruct
{
    int x;
    int y;
}
TestStruct;

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        TestStruct ts1 = {100, 50};
        NSLog(@"ts1: %p, %d, %d", &ts1, ts1.x, ts1.y);
        
        TestStruct ts2 = ts1;
        NSLog(@"ts2: %p, %d, %d", &ts2, ts2.x, ts2.y);

        TestObject* to1 = [[[TestObject alloc] init] autorelease];
        NSLog(@"to1: %p, %d, %d", to1, to1->x, to1->y);
        
        TestObject* to2 = to1;
        NSLog(@"to2: %p, %d, %d", to2, to2->x, to2->y);
        
    }
    return 0;
}

程序的運行結果如下:

ts1: 0x7fff63463898, 100, 50
ts2: 0x7fff63463890, 100, 50
to1: 0x7fc342d00370, 0, 0
to2: 0x7fc342d00370, 0, 0
程序代碼首先定義了一個類TestObject(繼承自NSObject),然後又定義了一個結構體TestStruct。這兩者都包含兩個整型的成員變量x和y。然後在main函數中,程序首先爲TestStruct結構體ts1分配內存空間,併爲其成員變量賦初值,x爲100,y爲50。然後通過NSLog函數打印出該結構體的地址和成員變量的值,即輸出的第一行內容。接着,程序執行了賦值語句,將ts1賦值給另一個TestStruct結構體對象ts2,這條語句會爲ts2分配另一塊內存,然後把ts1的每個成員變量的值複製過來。第二行輸出也可以看出來,地址不一樣了,所以如果修改ts1的成員變量的值,是不會影響ts2的。

接着再來看TestObject。程序接着使用alloc靜態方法分配了一塊新的內存空間,然後通過init實例方法進行初始化(所有成員變量的值爲0),最後將該內存空間的首地址返回。to1的實質就是一個指針,指向創建的TestObject對象。接着,程序將to1賦值給to2。to2也是一個指向TestObject對象的指針,其值與to1一樣,即兩者都指向同一個對象。所以在這種情況下,對to1的修改會同時影響to2。

二、對象拷貝

Foundation框架的NSObject類提供了兩個方法,分別是copy和mutableCopy方法,用於對NSObject對象進行拷貝操作。copy方法會調用NSCopying協議的copyWithZone:方法,而mutableCopy會調用 NSMutableCopying協議的mutableCopyWithZone:方法。將上面的代碼修改如下:

#import <Foundation/Foundation.h>

@interface TestObject : NSObject
{
    @public
    int x;
    int y;
}
@end

@implementation TestObject
- (NSString*)description
{
    return [NSString stringWithFormat:@"%@: %p, x: %d, y: %d", [self class], self, x, y];
}
@end

typedef struct TestStruct
{
    int x;
    int y;
}
TestStruct;

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {        
        TestObject* to1 = [[[TestObject alloc] init] autorelease];
        to1->x = 100; to1->y = 50;
        TestObject* to2 = [[[TestObject alloc] init] autorelease];
        to2->x = 200; to2->y = 400;
        TestObject* to3 = [[[TestObject alloc] init] autorelease];
        to3->x = 300; to3->y = 500;
        
        //創建包含to1, to2, to3的數組array1
        NSArray* array1 = [NSArray arrayWithObjects:to1, to2, to3, nil];
        NSLog(@"array1: %p, \n%@", array1, array1);
        
        //array2是array1調用copy的結果
        NSArray* array2 = [array1 copy];
        NSLog(@"array2: %p, \n%@", array2, array2);
        [array2 release];
        
        //mutableArray2是array1調用mutableCopy的結果
        NSMutableArray* mutableArray2 = [array1 mutableCopy];
        NSLog(@"mutableArray2: %@, %p, \n%@", [mutableArray2 class], mutableArray2, mutableArray2);
        [mutableArray2 removeLastObject];
        
        NSLog(@"After remove last object of mutableArray2");
        
        NSLog(@"array1: %p, \n%@", array1, array1);
        NSLog(@"array2: %p, \n%@", array2, array2);
        NSLog(@"mutableArray2: %p, \n%@", mutableArray2, mutableArray2);
        
        //mutableArray3是mutableArray2調用mutableCopy的結果
        NSMutableArray* mutableArray3 = [mutableArray2 mutableCopy];
        NSLog(@"mutableArray3: %p, \n%@", mutableArray3, mutableArray3);
        [mutableArray2 release];
        
        //array4是mutableArray3調用copy的結果
        NSArray* array4 = [mutableArray3 copy];
        NSLog(@"array4: %@, %p, \n%@", [array4 class], array4, array4);
        [mutableArray3 release];
        [array4 release];
    }
    return 0;
}


程序的運行結果如下:

2012-03-22 19:20:49.548 ObjectCopy[18042:403] array1: 0x7f9071414820, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400",
    "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.550 ObjectCopy[18042:403] array2: 0x7f9071414820, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400",
    "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.551 ObjectCopy[18042:403] mutableArray2: __NSArrayM, 0x7f9072800000, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400",
    "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.552 ObjectCopy[18042:403] After remove last object of mutableArray2
2012-03-22 19:20:49.552 ObjectCopy[18042:403] array1: 0x7f9071414820, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400",
    "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.553 ObjectCopy[18042:403] array2: 0x7f9071414820, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400",
    "TestObject: 0x7f9071414230, x: 300, y: 500"
)
2012-03-22 19:20:49.553 ObjectCopy[18042:403] mutableArray2: 0x7f9072800000, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400"
)
2012-03-22 19:20:49.557 ObjectCopy[18042:403] mutableArray3: 0x7f90729000d0, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400"
)
2012-03-22 19:20:49.558 ObjectCopy[18042:403] array4: __NSArrayI, 0x7f9071416e70, 
(
    "TestObject: 0x7f90714141b0, x: 100, y: 50",
    "TestObject: 0x7f90714141c0, x: 200, y: 400"
)

程序的運行結果有幾點值得注意,首先是array1與array2的地址相同,因爲NSArray對象在創建之後是不可以修改的。其次,NSArray的mutableCopy方法會返回一個NSMutableArray對象。第三,對於NSArray或者NSMutableArray來說,mutableCopy方法會創建新的可變數組對象,但其每個數組成員的值僅僅是原數組的一個指針賦值,這就是淺拷貝。而與之相對的則是深拷貝,即複製數組時不是複製數組每個元素的引用,而是創建一個與之相同的新對象。第四,在NSArray對象上調用mutableCopy方法返回一個NSMutableArray對象,而在NSMutableArray對象上調用copy方法則返回一個NSArray對象,而不是NSMutableArray對象。

當然,以上討論的是Foundation框架中的NSArray與NSMutableArray類,如果想要實現對自己創建的類的對象進行拷貝,則需要讓類實現NSCopying協議。

發佈了38 篇原創文章 · 獲贊 13 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章