NSDictionary-DeepMutableCopy.h:
#import <Foundation/Foundation.h>
@interface NSDictionary(DeepMutableCopy)
-(NSMutableDictionary *)mutableDeepCopy;
@end
类别DeepMutableCopy为NSDictionary实现新的方法,实现对NSDictionary的深层拷贝,
如果只是用NSMutableDictionary, 则此对象和源对象将会指向同一个对象,对copy对象修改的话,也会同时修改源对象。
NSDictionary-DeepMutableCopy.m:
#import "NSDictionary-DeepMutableCopy.h"
@implementation NSDictionary(DeepMutableCopy)
-(NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[self count]]; //预分配了一个self count大的空间来存储
NSArray *keys = [self allKeys];
for (id key in keys) //快速枚举,循环所有keys
{
id oneValue = [self valueForKey:key]; //设置oneValue为源值
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
创建了一个对NSDictionary对象的深层拷贝。如果对象没有相应mutableDeepCopy,那么它将尝试创建可变副本。
如果对象也没有相应mutableCopy消息,则按照常规方式创建副本。以确保对字典中包含的所有对象都创建了副本。
原来不是所有的对象都支持 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返回后是可以修改的对象
SectionsViewController.h:
#import <UIKit/UIKit.h>
@interface SectionsViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
{
UITableView *table;
UISearchBar *search;
NSDictionary *allNames;
NSMutableDictionary *names;
NSMutableArray *keys;
BOOL isSearching;
}
@property (nonatomic, retain) IBOutlet UITableView *table;
@property (nonatomic, retain) IBOutlet UISearchBar *search;
@property (nonatomic, retain) NSDictionary *allNames;
@property (nonatomic, retain) NSMutableDictionary *names;
@property (nonatomic, retain) NSMutableArray *keys;
- (void)resetSearch;
- (void)handleSearchForTerm:(NSString *)searchTerm;
@end
SectionsViewController.m:
#import "SectionsViewController.h"
#import "NSDictionary-DeepMutableCopy.h"
@implementation SectionsViewController
@synthesize table;
@synthesize search;
@synthesize allNames;
@synthesize names;
@synthesize keys;
#pragma mark -
#pragma mark Custom Methods
- (void)resetSearch {
NSMutableDictionary *allNamesCopy = [self.allNames mutableDeepCopy];//字典对象进行深层拷贝
self.names = allNamesCopy;
[allNamesCopy release];
[keyArray addObject:UITableViewIndexSearch];//放大镜
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.allNames allKeys]
sortedArrayUsingSelector:@selector(compare:)]];//按字幕顺序进行排序后,将字典对象中所有的键存入可变数组
self.keys = keyArray;
[keyArray release];
}
取消搜索或者更改搜索条件时调用此方法。它所作的就是创建allnames的可变副本。返回的names和keys都是可变的!
- (void)handleSearchForTerm:(NSString *)searchTerm
{
NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init];//准备删除的区块
[self resetSearch];
for (NSString *key in self.keys) {//遍历可变数组keys
NSMutableArray *array = [names valueForKey:key];//将所有keys中的key存入array
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in array) {//遍历装有所有key的数组
if ([name rangeOfString:searchTerm
options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}//如果输入的在array中没有找到,则将该条记录存入待删除数组
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
//如果某一区块内所有的要删除数据的长度等于区块内所有数据的长度,
//证明搜索的内容不在该区块中,则这个区块为空,存入待删除区块中
[array removeObjectsInArray:toRemove];//删除待删除数组
[toRemove release];
}
[self.keys removeObjectsInArray:sectionsToRemove];//删除待删除区块
[sectionsToRemove release];
[table reloadData];//重新加载table
}
- (void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:@"sortednames"
ofType:@"plist"];//获取文件路径
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:path];//从文件中加载数据
self.allNames = dict;
[dict release];
[self resetSearch];
[table reloadData];
[table setContentOffset:CGPointMake(0.0, 44.0) animated:NO];//设置偏移值为44像素,即搜索栏高度。
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.names = nil;
self.keys = nil;
self.table = nil;
self.search = nil;
self.allNames = nil;
}
- (void)dealloc {
[names release];
[keys release];
[table release];
[search release];
[allNames release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [keys count];
}//区块的数量是由数组内数据的数量决定的,即keys数组中有多少值(A、B、C...)
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if ([keys count] == 0)
return 0;
NSString *key = [keys objectAtIndex:section]; //被选取的某一个区块的值,比如A区块值即为A
NSArray *nameSection = [names objectForKey:key];//在数据字典中键为A的数组的数量,即为A区块中行数
return [nameSection count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SectionsTableIdentifier ];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier: SectionsTableIdentifier ] autorelease];
}
cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;
}//构建每一行的cell
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{
if ([keys count] == 0)
return nil;
NSString *key = [keys objectAtIndex:section];
if (key == UITableViewIndexSearch)
return nil;
return key;
}//设置每一个区块的标题,该区块所有数据的键均为A,则标题为A
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
if (isSearching)
return nil;
return keys;
}//设置搜索时索引为隐藏
#pragma mark -
#pragma mark Table View Delegate Methods
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[search resignFirstResponder];
search.text = @"";
isSearching = NO;
[tableView reloadData];
return indexPath;
}//如果用户在食用搜索栏时单击一行,则关闭键盘
- (NSInteger)tableView:(UITableView *)tableView
sectionForSectionIndexTitle:(NSString *)title
atIndex:(NSInteger)index
{
NSString *key = [keys objectAtIndex:index];
if (key == UITableViewIndexSearch)
{
[tableView setContentOffset:CGPointZero animated:NO];
return NSNotFound;
}
else return index;
}//如果用户点击放大镜,则返回NSNotFound,表视图收到此相应,就知道需要滚至顶部,取消了偏移!
#pragma mark -
#pragma mark Search Bar Delegate Methods
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
NSString *searchTerm = [searchBar text];
[self handleSearchForTerm:searchTerm];
}//搜索方法
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
isSearching = YES;
[table reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchTerm
{
if ([searchTerm length] == 0)
{
[self resetSearch];
[table reloadData];
return;
}
[self handleSearchForTerm:searchTerm];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
isSearching = NO;
search.text = @"";
[self resetSearch];
[table reloadData];
[searchBar resignFirstResponder];
}
@end