copy修飾NSArray strong修飾NSMutableArray

strongcopy修飾符

  • strong修飾的屬性,對該屬性賦值時發生指針拷貝,即淺拷貝;
  • copy修飾的屬性,對該屬性賦值時發生內容拷貝,即深拷貝。(存在特殊Case)

通過重寫對象的setter方法實現。

當將不可變對象賦值給copy修飾的不可變對象時,也是發生淺拷貝。這算是一種優化,驗證見下文。

對數組而言,淺拷貝即拷貝了指向數組的指針;深拷貝即拷貝了數組中存儲的內容,而這個深拷貝是通過調用copy方法實現。

copymutableCopy方法

以下代碼發生崩潰,爲什麼?

@property (copy, nonatomic) NSMutableArray *copAry;

- (void)testCrash {
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];
    self.copAry = arr;
    [self.copAry removeObject:@1]; // 直接崩潰
    NSLog(@"self.copyAry = %@", self.copAry);
}

arr賦值給self.copAry,依據上面原則,會調用copy方法實現深拷貝。

注意: OC中不管是集合類對象(如:NSArrayNSMutableArrayNSSet…)還是非集合類對象(如:NSString…)接收到copymutableCopy消息時遵循以下原則:

  • copy方法返回的都是不可變對象,因此如上代碼中,self.copAry即爲不可變對象,對不可變對象調用可變對象的方法就會Crash;
  • mutableCopy方法返回的都是可變對象

因此,如果我們使用strong來修飾self.copAry,則在self.copAry = arr;時發生淺拷貝,只是拷貝了arr的指針,還是指向的原數組,還是可變對象。便不會發生Crash。

NSArray爲什麼需要用copy來修飾?

@property (strong, nonatomic) NSArray *arr1;
@property (copy, nonatomic) NSArray *arr2;

// 爲什麼 NSArray 需要使用 copy
- (void)testUseCopyWithAry {
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@(1), @(2), @(3), nil];
    self.arr1 = arr;
    self.arr2 = arr;
    
    [arr addObject:@(4)];
    NSLog(@"arr1 = %@, arr2 = %@", self.arr1, self.arr2);
    
    /* 輸出結果:
     arr1 = (
         1,
         2,
         3,
         4
     ), arr2 = (
         1,
         2,
         3
     )
     */
}

如果對一個strong修飾的NSArray對象A賦值一個NSMutableArray對象B,對於A對象而言它是不可變的,但是使用strong修飾,賦值時發生了淺拷貝,A、B對象指向了同一個數組,因此對B的修改將會影響到A(如上代碼)。一般這並不是我們期望的,因此我們使用copy來修飾NSArray對象來避免上述情況。

對象間賦值時測試例子

 - (void)test01 {
    NSArray *array = @[@1,@2,@3,@4];
    
    NSArray *copyArr = [array copy];
    NSArray *mCopyArr = [array mutableCopy];
    
    NSMutableArray *mcArr = [array copy];
    NSMutableArray *mmCopyArr = [array mutableCopy];
    
    NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
    
    /*  輸出結果
     array:     0x60000024ce10
     copyArr:   0x60000024ce10
     mCopyArr:  0x60000024cd80
     mcArr:     0x60000024ce10
     mmCopyArr: 0x60000024ce70
     */
    
}

 - (void)test02 {
    NSArray *tarray = @[@1,@2,@3,@4];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    [array addObjectsFromArray:tarray];
    
    NSArray *copyArr = [array copy];
    NSArray *mCopyArr = [array mutableCopy];
    
    NSMutableArray *mcArr = [array copy];
    NSMutableArray *mmCopyArr = [array mutableCopy];
    
    NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
    /* 輸出結果
     array:     0x60000024cd20
     copyArr:   0x60000024cc90
     mCopyArr:  0x60000024ce40
     mcArr:     0x60000024cde0
     mmCopyArr: 0x60000024d020
     */
}

總結:

  • 不可變對象(如:NSArray對象)發送copy消息,返回不可變對象,這其中並沒有發生深拷貝,僅是拷貝了指針而已;
  • 不可變對象發送mutableCopy消息,返回可變對象;對可變對象發送copy消息,返回不可變對象;對可變對象發送mutableCopy消息,返回可變對象,這其中均發生了深拷貝,即拷貝了對象的內容。

當然在 iOS 中並不是所有的對象都支持copymutableCopy,遵守 NSCopying協議的類可以發送 copy消息,遵守NSMutableCopying 協議的類纔可以發送 mutableCopy消息。
假如發送了一個沒有遵守上訴兩協議而發送 copy或者 mutableCopy, 那麼就會發生異常。但是默認情況下iOS中的類並沒有遵守上述兩個協議。

如果想使用copy那麼就必須遵守NSCopying, 並且實現 copyWithZone:方法,如果想使用mutableCopy那麼就必須遵守 NSMutableCopying, 並且實現 mutableCopyWithZone:方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章