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:方法。

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