iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题


    在iOS5学习过程中,表视图一章节中最难的莫过于搜索栏实现过程中的深拷贝浅拷贝一环节。现在讨论一下iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题

   要为一个tableView实现搜索功能的时候,遇到了一个问题,学习了好长时间终于想通,现在将问题以及我的理解总结一下.

 (相关实例《iOS5开发基础教程》最新版的“08 - Sections2”下载地址:http://vdisk.weibo.com/s/hBHg6

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第1张图片

文中实例运行的编译环境为:

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第2张图片

在ObjC中,什么是深浅拷贝?
深浅拷贝分别指深拷贝和浅拷贝,即mutableCopy和copy方法。
copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象。

什么时候用到深浅拷贝?下面举几个例子说明。


非容器类对象

如NSString,NSNumber等一类对象
示例1:

   // 非容器类对象
    NSString *str = @"Chen_Yilong";
    NSString *strCopy = [str copy];
    NSMutableString *mstrCopy = [str mutableCopy];
    [mstrCopy appendString:@"的CSDN博客专栏"];
    NSLog(@"%@",mstrCopy);
    NSLog(@"%@",str);
    NSLog(@"%@",strCopy);
运行结果:

Chen_YilongCSDN博客专栏

Chen_Yilong

Chen_Yilong

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第3张图片

如果我们试图向strCopy 追加字符串时,编译器xcode会报错:由此可见copy复制后的不可变对象的副本不可变。

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第4张图片




查看内存可以发现,str和strCopy指向的是同一块内存区域,我们称之为弱引用(weak reference)。而mstrCopy是真正的复制,系统为其分配了新内存空间,保存从str复制过来的字符串值。从最后一行代码中修改这些值而不影响str和strCopy中可证明。

示例2:

    NSMutableString *mstr = [NSMutableString stringWithString:@"Chen_Yilong"];
    NSString *strCopy = [mstr copy];
    NSMutableString *mstrCopy = [mstr copy];
    NSMutableString *mstrMCopy = [mstr mutableCopy];
    //[mstrCopy appendString:@"的CSDN专栏http://blog.csdn.net/Chen_Yilong"];  //error
    [mstr appendString:@"的新浪微博http://weibo.com/luohanchenyilong"];
    [mstrMCopy appendString:@"的新浪微博帐号为:南阳理工微博校园"];

    NSLog(strCopy);
    NSLog(mstr);
    NSLog(mstrCopy);
    NSLog(mstrMCopy);

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第5张图片

第一次运行出现错误:在

  [mstrCopy appendString:@"CSDN专栏http://blog.csdn.net/Chen_Yilong"];  //error

处出错,原因是如果是对可变对象复制,都是深拷贝,但copy复制返回的对象是不可变的。注释此行,运行成功:

结果为

 Chen_YilongCSDN博客[718:f803] Chen_Yilong

 Chen_YilongCSDN博客[718:f803] Chen_Yilong的新浪微博http://weibo.com/luohanchenyilong

 Chen_YilongCSDN博客[718:f803] Chen_Yilong

Chen_YilongCSDN博客[718:f803] Chen_Yilong的新浪微博帐号为:南阳理工微博校园

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第6张图片

以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错。这点要注意,好好理解。

非容器类复制情况小结
对于非容器类对象,有:

  • 如果对一个不可变对象复制,copy是指针复制,即浅拷贝;而mutableCopy则是对象复制,即深拷贝。(示例1)
  • 如果是对可变对象复制,都是深拷贝,但copy复制返回的对象是不可变的。(示例2)


  • 如下图所示:对于非容器类:

  • (1)一般情况下, copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象。copy不可变浅复制,mutableCopy可变深复制;
  •          特殊情况下:
  •        (A)可变对象的                Copy:复制:(a)副本不可变(欲求证,可见前面的示例2)(b)深复制
  •        (B)不可变对象的mutableCopy:复制:(a)副本可变                                                    (b)深复制
  •          由(1)(2)可知这两种特殊情况都是深复制;
  •          研究一下(B):用mutableCopy:复制不可变对象,由(3)可知:得到的副本一定是可变,所以可知道这种复制一定不是指针复制,为深层复制,否则依然不可变.
    • (2)可变对象不管怎么复制都是深复制,且副本不一定可变(图片第二列);                                            
    • (3)用mutableCopy:复制的副本可变,且都是深复制(图片第三行);                                        
    • (4)用             Copy:复制的副本不可变,不一定是浅复制;(图片第二行)                                          
    • (5)副本不可变,不一定就是浅复制(如:可变-Copy,唯一副本不可变的深复制);副本可变,一定是深复制(图片第三行)                                   


  • 于是知道:
  •          可变不可变,看复制方法是mutablCopy:或Copy:;
  •           深或浅,看对象可变还是不可变;
iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第7张图片


容器类对象深浅复制

比如NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也适用的,下面探讨的是复制后容器内对象的变化。

示例3

 /* copy返回不可变对象,mutablecopy返回可变对象 */
    NSArray *array1     = [NSArray arrayWithObjects:@"Chen",@"Chen_Yi",@"long",nil];
    NSArray *arrayCopy1 = [array1 copy];
    //arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针
    NSLog(@"array1 retain count: %d",[array1 retainCount]);
    NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);
    
    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
    //mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的还是同一个对象。mArrayCopy1还可以修改自己的对象
    [mArrayCopy1 addObject:@"的新浪微博是:南阳理工微博校园"];
    [mArrayCopy1 removeObjectAtIndex:0];
    int count=[mArrayCopy1 count];
    int i;
    for(i=0;i<count;i++)
    {
        printf("%i:  %s\n",i,[[mArrayCopy1 objectAtIndex:i] UTF8String]);
    } 
运行结果:

 array1 retain count: 2

 array1 retain count: 2

0:  Chen_Yi

1:  long

2:  的新浪微博是:南阳理工微博校园



iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第8张图片
array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,符合前面示例1讨论的结论。mArrayCopy1可以改变其内的元素:删除或添加。但容器内的元素内容都是浅拷贝。

示例4

  NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"Chen_Yilong"],@"Chen",@"_Yilong",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 = @"Chen_Yilong";//这样会改变testString的指针,其实是将@“Chen_Yilong”临时对象赋给了testString  
    [testString appendString:@" 的新浪微博是:南阳理工微博校园"];//这样以上三个数组的首元素都被改变了
    NSLog(@"%@",testString);
    NSLog(@"%@",[mArray1 objectAtIndex:0]);
    NSLog(@"%@",[mArrayCopy2 objectAtIndex:0]);
    NSLog(@"%@",[mArrayMCopy1 objectAtIndex:0]);
运行结果:

 mArray1 retain count: 1

 mArray1 retain count: 2

mArray1 retain count: 2

 Chen_Yilong 的新浪微博是:南阳理工微博校园

Chen_Yilong 的新浪微博是:南阳理工微博校园

Chen_Yilong 的新浪微博是:南阳理工微博校园

 Chen_Yilong 的新浪微博是:南阳理工微博校园


iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第9张图片

由此可见,对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

示例5

 NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSString stringWithString:@"b"],@"c",nil];
    NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
    NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
    [NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。
或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

举个例子,[[array objectAtIndex:0] appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝。

用下面的代码两个例子来分别验证可变元素与不可变元素在两种方法的拷贝后的区别:

验证一:可变元素深层复制

 NSString *str = @"Chen_Yilong";
    NSMutableString *mstrCopy = [str mutableCopy];
    
    NSLog(@"%@",mstrCopy);
    NSLog(@"%@",str);

    NSMutableArray *array = [NSMutableArray arrayWithObjects: mstrCopy,str,nil];
    NSArray *deepCopyArray=[[NSArray alloc] initWithArray:array copyItems: YES];
    NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
                                         [NSKeyedArchiver archivedDataWithRootObject:array]];
    [[array objectAtIndex:0] appendString:@"的CSDN博客专栏"];
    NSLog(@"%@",mstrCopy);
    int i;
    int count = [deepCopyArray count];
    for (i=0; i<count; i++) {
        printf("The         num%i       of        deepCopyArray is :  %s\n",i,[[deepCopyArray objectAtIndex:i] UTF8String]);
        printf("The         num%i       of    trueDeepCopyArray is :  %s\n",i,[[trueDeepCopyArray objectAtIndex:i] UTF8String]);
    }

运行结果

Chen_Yilong

 Chen_Yilong

Chen_YilongCSDN博客专栏

The         num0       of            deepCopyArray is :  Chen_Yilong

The         num0       of    trueDeepCopyArray is :  Chen_Yilong

The         num1       of            deepCopyArray is :  Chen_Yilong

The         num1       of    trueDeepCopyArray is :  Chen_Yilong


iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第10张图片

先复制,再改变mstrCopy的内容,两个复制对象都没有收到影响,于是我们知道:

deepCopyArray内的可变元素其还是深复制。

即不可变容器类对象中的可变元素接收deepCopyArray复制之后,是可变的,属于深复制。

验证二:不可变元素指针复制

NSArray *mArray= [NSArray arrayWithObjects:@"Chen",@"_Yilong",nil];  
    NSLog(@"Original    Array  retain  count   is: %d",[mArray retainCount]);  
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects: mArray,nil];
    NSLog(@"NeverCopyied  Array  retain  count is: %d",[mArray retainCount]);  
    
    NSArray *deepCopyArray=[[NSArray alloc] initWithArray:array copyItems: YES];
    NSLog(@"deepCopyied   Array  retain  count is: %d",[mArray retainCount]);  

    NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
                                         [NSKeyedArchiver archivedDataWithRootObject:array]];
    NSLog(@"trueDeepCopyied Array retain count is: %d",[mArray retainCount]);  

运行结果:

 Original              Array  retain  count   is: 1

 NeverCopyied     Array  retain  count is: 2

 deepCopyied       Array  retain  count is: 3

 trueDeepCopyied Array retain count is: 3


iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第11张图片

mArray在deepCopy后引用加一,由2变3,说明是指针复制;

在trueDeepCopy后引用没变,原来是3,现在还是3,说明是深层复制;

容器类复制情况总结:

将以上的总结制成一张表,其中我们的研究对象分两类,一类:第一行:容器类;另一类:容器类中的成员;我在这里为了说明这四种复制方法的区别,我引入一个新的概念“深拷贝效率”,也就是“蓝色个数/(蓝色+黄色)个数”,也就是“复制后成员为深复制(深副本)的个数/原成员个数“。

即如下图所示:

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第12张图片



自己实现深拷贝的方法

NSDictionaryMutableDeepCopy.h

#import <foundation /Foundation.h>

@interface NSDictionary(MutableDeepCopy)

- (NSMutableDictionary *)mutableDeepCopy;

@end
</foundation>

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;
}

@end


而《iOS5开发基础教程》表视图一章中就是这样实现的,现附上源码:


NSDictionary-MutableDeepCopy.h

#import <Foundation/Foundation.h>
@interface NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy;
@end

NSDictionary-MutableDeepCopy.m

#import "NSDictionary-MutableDeepCopy.h"

@implementation NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy {
    NSMutableDictionary *returnDict = [[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];
        [returnDict setValue:oneCopy forKey:key];
    }
    return returnDict;
}


@end


而如果你要复制的对象是一个plist文件,并且严格遵守一个键对应一个数组,并且数组中均为字符串,就像这本书中的例子一样,我们也可以这样写.m文件

#import "NSDictionary-MutableDeepCopy.h"

@implementation NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy {
    NSMutableDictionary *returnDict = [NSMutableDictionary dictionaryWithCapacity:[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)])
        if ([oneValue respondsToSelector:@selector(mutableCopy)]) 
        oneCopy = [oneValue mutableCopy];    
//        if (oneCopy == nil)      //数组均不为空,所以不需要考虑这种情况
//            oneCopy = [oneValue copy];
        [returnDict setValue:oneCopy forKey:key];
    }
    return returnDict;
}

而这一种mutableCopy方法如何确保:

   a的副本被删除或者改变的时候不会影响到a自己的内容呢?

答:NSString NSArray 经mutableCopy方法复制后就变成了可变副本。


书本中的例子给了我们三种情况,

 if ([oneValue respondsToSelector:@selector(mutableDeepCopy)])
         oneCopy = [oneValue mutableDeepCopy];
 else if ([oneValue respondsToSelector:@selector(mutableCopy)])
        oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
        oneCopy = [oneValue copy];
可是我们却只会用到其中一种,大可将其余两种多余的删除掉,

因为书本复制的对象是一个plist文件,并且严格遵守一个键对应一个数组,并且数组中均为字符串,所以我们也可以这样写.m文件

//        if ([oneValue respondsToSelector:@selector(mutableDeepCopy)])//本实例数组中没有字典类型所以不需要对字典进行遍历
//           oneCopy = [oneValue mutableDeepCopy];
//        else if ([oneValue respondsToSelector:@selector(mutableCopy)])
        if ([oneValue respondsToSelector:@selector(mutableCopy)]) 
        oneCopy = [oneValue mutableCopy];
//        if (oneCopy == nil)//数组均不为空,所以不需要考虑这种情况
//            oneCopy = [oneValue copy];

那什么时候才会用到其余两种情况呢?

当数组为空(nil)时会用到

//        if (oneCopy == nil)//数组均不为空,所以不需要考虑这种情况
//            oneCopy = [oneValue copy];

当字典的值--数组中还有字典的时候会用到

//        if ([oneValue respondsToSelector:@selector(mutableDeepCopy)])//本实例数组中没有字典类型所以不需要对字典进行遍历
//            oneCopy = [oneValue mutableDeepCopy];

这个方法是针对字典的而设立的,并且只有字典才能响应这个方法。为什么呢?

NSString 和NSArray 要由mutableCopy进行处理。为什么呢?

下面对这两个问题做一下解答:

观察一下.m文件的开头,

@implementation NSDictionary(MutableDeepCopy)
- (NSMutableDictionary *)mutableDeepCopy ;
于是就知道,这不是一般的方法,在NSDictionary 后面加了一个“()”,这是什么意思?这是一种全新的方法,叫分类方法。就是将NSDictionary 额外地添加了一种函数,即扩展出了一种功能,所说的就是这个mutableDeepCopy函数,所以mutableDeepCopy方法是隶属于NSDictionary类的,是 NSDictionary的分类方法,所以是针对 NSDictionary类型数据进行的深拷贝。(关于分类方法可以参考:
点击打开链接

    并且这也是一个递归方法,可能不好理解,为什么我们自定义了一个函数,在自己定义的函数中还要去调用自己呢?而这就叫递归。

    为了理解什么叫做递归,我们做一个比喻,我们将需要深复制的字典比作一个压缩包,深复制方法就好像解压缩软件,字典中的值相当于压缩包中的文件,但压缩包中的文件,也可能出现这种情况:压缩包中的文件还是压缩包。也就是说:已经把压缩包用解压缩软件解压了,可是为了解压“缩压缩包中的压缩包”,我们还是得用刚用到的解压缩软件,就好像我们为了深复制字典中出现的字典,甚至字典中的字典的字典,还得用同一个方法:mutableDeepCopy。而这,就叫递归!

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第13张图片




     那为什么说只需要下面的一种方法就足够了呢?

    if ([oneValue respondsToSelector:@selector(mutableCopy)]) 
        oneCopy = [oneValue mutableCopy];
      首先,你要了解Foundation类,NSArray  NSMutableArray NSMutableDictionary NSDctionary NSString都是Foundation类。而Foundation类已经遵守了<NSCopying>和 <NSMutableCopying>协议,即实现了copy和mutableCopy方法,因此Foundation对象可以使用这些方法创建对象的副本或可变副本。(更多Foundation类信息,可参照 点击打开链接






使用类别方法来实现

自定义对象

如果是我们定义的对象,那么我们自己要实现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

原来不是所有的对象都支持 copy
只有遵守NSCopying 协议的类才可以发送copy消息
只有遵守 NSMutableCopying 协议的类才可以发送mutableCopy消息

假如发送了一个没有遵守上诉两协议而发送 copy或者 mutableCopy,那么就会发生异常

默认 nsobject没有遵守这两个协议
但是 copy和mutableCopy这两个方法是nsobject定义的

如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法
如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法

看了一下几个遵守 NSCopying协议的基本上是一些基础核心类
比如 NSString NSNumber

copy以后,就是返回一个新的类, 你要负责释放掉,原先被拷贝的retaincount没有+1 所以,不需要负责释放

copy和mutableCopy 就是copy返回后的是不能修改的对象, mutableCopy返回后是可以修改的对象

下面是实现一个copyWithZone的例子

@interface BankAccount: NSObject <NSCopying>
{
double accountBalance;
long accountNumber;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(double) getAccountBalance;
-(long) getAccountNumber;
-(void) setAccountBalance: (double) x;
-(void) setAccountNumber: (long) y;
-(void) displayAccountInfo;
-(id) copyWithZone: (NSZone *) zone;
@end


-(id) copyWithZone: (NSZone *) zone
{
BankAccount *accountCopy = [[BankAccount allocWithZone: zone] init];

[accountCopy setAccount: accountNumber andBalance: accountBalance];
return accountCopy;
}



深度拷贝和浅拷贝
上面的方法是浅拷贝,意思就是,只是重新分配了BankAccount类的 内存,并没有对BankAccount的属性重新分配内存
两个BankAccount的属性都是指向同一个地方的. 修改了其中一个BankAccount属性,那么另外一个BankAccount属性
也会一起发生变化 

深度拷贝
深度拷贝这里用到一个存档功能,先把原先的存档(其实就是序列化对象,然后存到一个文件,等下可以反序列化出来赋值给另外一个对象),然后存到另外的而一个对象 中
NSKeyedArchiver 序列化类
NSKeyedUnarchiver 反序列化类

NSArray *myArray1;
NSArray *myArray2;
NSMutableString *tmpStr;
NSMutableString *string1;
NSMutableString *string2;
NSMutableString *string3;
NSData *buffer;

string1 = [NSMutableString stringWithString: @"Red"];
string2 = [NSMutableString stringWithString: @"Green"];
string3 = [NSMutableString stringWithString: @"Blue"];
myArray1 = [NSMutableArray arrayWithObjects: string1, string2, string3, nil];
buffer = [NSKeyedArchiver archivedDataWithRootObject: myArray1];
myArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
tmpStr = [myArray1 objectAtIndex: 0];
[tmpStr setString: @"Yellow"];

NSLog (@"First element of myArray1 = %@", [myArray1 objectAtIndex: 0]);
NSLog (@"First element of myArray2 = %@", [myArray2 objectAtIndex: 0]);

iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题_第14张图片
其中

    tmpStr = [myArray1 objectAtIndex: 0];

属不属于浅复制?不属于,这是简单的使指针[myArray1 objectAtIndex0]的指向与 tmpStr 指向相同内存。结果就是造成了上面的alloc下动态分配。这就引发一个问题:复制对象有实际作用吗?

有的人可能会说:

复制对象有实际作用吗?比如说上面的语句就是:

两个对象ab;要把a复制给b;直接用ba;不就好了?



如两个对象

NSString *str1=[[NSString alloc] initWithString:@"string1"];

NSString *str2=[[NSString alloc] initWithString:@"string2"];

str2=str1;  //使指针str2的指向与str1指向相同内存。结果就是造成了上面的alloc下动态分配

的内存,内存泄露.当调用[str1 release];  [str2 release];内存的释放最终调用的是[str1 dealloc];

或者是[str2 dealloc],什么情况下才会调用这个释放内存的dealloc方法呢?当创建的对象其引用计数(retainCount)0时,就会调用。

而哪些方法会造成引用计数改变呢?

1.调用alloc方法

2.调用copyretain 特性

3.relase方法

str2=[str1 copy]; 引用计数加1,所以当调用[str1 release];str2str1指向的共同对象并未析构,就是还存在。如果用直接复制str2=str1就不存在了。

引用计数变化为: 1.调用alloc使引用计数加1,变为了1.

2.调用copy,引用计数加1,变为2.

3.调用release,引用计数减1,变为1所以引用计数不为0,不会调用dealloc释放内存

的函数,所以内存还存在,如果要释放内存,必须再release一次。

所以这才是两者之间根本区别。

C语言解释就是,新分配一块内存空间来存储要复制的值。



cocoa 的属性也有3种
protected - Access is allowed only by methods of the class and any subclasses. 默认是这个

private - Access is restricted to methods of the class. Access is not available to subclasses. 
public - Direct access available to methods of the class, subclasses and code in other module files and classes.
public的变量可以用 -> 操作符访问

参 考地址:http://www.techotopia.com/index.php/Copying_Objects_in_Objective-C
http://wenku.baidu.com/view/12d6592fb4daa58da0114af0.html
http://www.techotopia.com/index.php/Objective-C_-_Data_Encapsulation,_Synthesized_Accessors_and_Dot_Notation
关 于访问器的详细文档
http://cocoacast.com/?q=node/103
本文参考了网页http://www.cnblogs.com/pengyingh/articles/2352908.html

你可能感兴趣的:(Objective-C)