欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

ios学习路线—Objective-C(深浅拷贝)

程序员文章站 2022-03-24 12:28:36
在ObjC中,什么是深浅拷贝? 深浅拷贝分别指深拷贝和浅拷贝,即 mutableCopy 和 copy 方法。 copy复制一个不可变对象,而 mutableCopy 复制一个 mutable 可变对象。 非容器类对象 如NSString,NSNumber等一类对象 示例1: 查看内存可以发现,st ......

在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