在学习iphone开发教程的中第8章(也就是《iOS5开发基础教程》最新版的“08 - Sections2”下载地址:http://vdisk.weibo.com/s/hBHg6)
要为一个tableView实现搜索功能的时候,遇到了一个问题,学习了好长时间终于想通,现在将问题以及我的理解总结一下.
《iOS5开发基础教程》
书上讲要实现一个MutableDeepCopy协议,关于这个整个思路,协议,需要一个新的副本这些我都能明白,就是里头的具体实现方法遇到点问题,里头的实现代码如下,
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
书上的解释是,
在for里头迭代每个数组,
首先尝试创建深层可变副本(mutableDeepCopy),如果不行,再尝试创建可变副本(mutableCopy),再不行再创建常规副本(copy),这样保证每个对象都创建了副本。
我的问题是,如果对象a尝试mutableDeepCopy失败,接着去创建可变副本甚至常规副本,
那么这个副本还是深层可变副本吗,不是的话,他怎么能保证a的副本被删除或者改变的时候不会影响到a自己的内容呢?
首先,书本中的例子给了我们三种情况,
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。而这,就叫递归!
那为什么说只需要下面的一种方法就足够了呢?
if ([oneValue respondsToSelector:@selector(mutableCopy)]) oneCopy = [oneValue mutableCopy];首先,你要了解Foundation类,NSArray NSMutableArray NSMutableDictionary NSDctionary NSString都是Foundation类。而Foundation类已经遵守了<NSCopying>和 <NSMutableCopying>协议,即实现了copy和mutableCopy方法,因此Foundation对象可以使用这些方法创建对象的副本或可变副本。(更多Foundation类信息,可参照点击打开链接)
如果你要复制的对象是一个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方法复制后就变成了可变副本。
如果想深入理解,这就涉及到深拷贝和浅拷贝的问题,这个问题我在上篇文章中已经进行了详细论述,请点击打开链接:iOS开发中表视图搜索栏实现中的Objective-C 深浅拷贝问题