淺析Objective-C的copy


一直對NSObject的copy方法似懂非懂,今天工作做完了,整理一下

深復制和淺復制是什么在這里就不贅述

今天主要分三種類型對copy進行探討:系統非容器類對象、系統容器類對象和自定義對象

系統非容器類對象(NSString,NSNumber等)

NSString *str1 = @"123";
NSString *str2 = [str1 copy];
NSString *str3 = [str1 mutableCopy];
NSMutableString *str4 = [str1 copy];
NSMutableString *str5 = [str1 mutableCopy];
//    [str3 appendString:@"456"];   //編譯出錯
//    [str4 appendString:@"456"];   //運行出錯

如上代碼,打印結果如下圖

 

結論:

1.對比所有string的地址,發現copy返回的是淺復制(只復制了指針),mutableCopy返回的是深復制(重新分配了內存)

2.str3在編譯時為NSString,運行時為NSMutableString類型,從圖中str3的類型為__NSCFString可證明,所以修改操作在編譯的時候就會報錯

3.str4在編譯時為NSMutableString,運行時為NSString類型,從圖中str4的類型為__NSCFConstantString可證明。所以對str4進行append等修改,編譯可通過,但運行時會直接crash

 

系統容器類對象(NSArray,NSDictionary等)

NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"1"], @"2", nil];
NSArray *array2 = [array1 copy];
NSArray *array3 = [array1 mutableCopy];
NSMutableArray *array4 = [array1 copy];
NSMutableArray *array5 = [array1 mutableCopy];

結論:

array1-array5的原理同NSString部分,但是注意mutableCopy返回的深復制,是對容器的深復制,容器里的元素仍然是淺復制

 

疑問:那如何對容器內的元素也進行深復制呢?

第一種方法:使用initWithArray:copyItems:方法

NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"1"], @"2", nil];
NSArray *array6 = [[NSArray alloc] initWithArray:array1 copyItems:YES];

 

此時array6的第一個元素是array1的第一個元素的深復制,但是第二個元素則是淺復制。

莫非用官方的api也會出錯?其實不是的,因為對於不可變對象,對其進行淺復制就足夠,因為你改變不了其值!

所以initWithArray:copyItems:可以實現一種不夠徹底的深復制

疑問:如果真的需要對容器中的不可變對象進行深復制,那怎么辦?

第二種方法:使用NSKeyedUnarchiver

NSArray *array7 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array1]];

 

NSKeyedUnarchiver是真正意義上的深復制

 

自定義對象

 以上,NSString和NSArray都是默認實現了NSCopying和NSMutableCopying協議,才能響應copy和mutableCopy方法

如果是自定義對象,那么怎么讓其響應copy和mutableCopy方法呢?

上代碼

//
//  MyObj.h
//  No
//
//  Created by Norcy on 16/1/28.
//  Copyright © 2016年 Norcy. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface MyObj : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic, retain) NSMutableString *str1;
@property (nonatomic, retain) NSString *str2;
@property (nonatomic, assign) int num;
@end
MyObj.h

  

//
//  MyObj.m
//  No
//
//  Created by Norcy on 16/1/28.
//  Copyright © 2016年 Norcy. All rights reserved.
//

#import "MyObj.h"

@implementation MyObj
- (id)init
{
    if (self = [super init])
    {
        self.str1 = [[NSMutableString alloc] init];
        self.str2 = [[NSString alloc] init];
        self.num = -1;
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone
{
    MyObj *obj = [[[self class] allocWithZone:zone] init];
    obj.str1 = [self.str1 copyWithZone:zone];
    obj.str2 = [self.str2 copyWithZone:zone];
//    obj.str1 = [self.str1 copy];  //這樣寫也可以
//    obj.str2 = [self.str2 copy];
    obj.num = self.num;
    return obj;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    MyObj *obj = [[[self class] allocWithZone:zone] init];
    obj.str1 = [self.str1 mutableCopyWithZone:zone];
    obj.str2 = [self.str2 mutableCopyWithZone:zone];
//    obj.str1 = [self.str1 mutableCopy];   //這樣寫也可以
//    obj.str2 = [self.str2 mutableCopy];
    obj.num = self.num;
    return obj;
}
@end
MyObj.m

 

調用方法如下:

MyObj *obj = [[MyObj alloc] init];
obj.str1 = @"123";
obj.str2 = @"345";
obj.num = 1;
MyObj *obj2 = [obj copy];
MyObj *obj3 = [obj mutableCopy];

 

如上代碼,打印結果如下圖


可以看到

(1)copy方法准確返回了一個新的對象,且對象的屬性是淺復制

(2)mutableCopy方法准確返回了一個新的對象,且對象的屬性是深復制

(3)想要實現copy方法,需要讓對象聲明遵循<NSCopying>和<NSMutableCopying>協議,並且實現實現copyWithZone:和mutableCopyWithZone:方法

 (注意哦,不是實現copy和mutableCopy方法;還有zone參數是什么鬼?其實zone參數的存在是歷史原因,現在我們不必太過關心它)

(4)以下這2種寫法在該情況下是等價的

    obj.str1 = [self.str1 copyWithZone:zone];
    obj.str2 = [self.str2 copyWithZone:zone];
// obj.str1 = [self.str1 copy]; //這樣寫也可以 // obj.str2 = [self.str2 copy];

 

參考文章:

《淺談copy和retain》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM