做 ios 开发,NSDictionary、NSMutableDictionary,NSMutableArray、NSArray 都是很常用的容器类
Array 就不多做讨论了,今天的文章主要讨论 NSDictionary 和 NSMutableDictionary~
以往我用 cocoa 的 Dictionary 的时候,都是选择用 NSString 来作为键对象的类型。
一直都没有出什么大的问题,用起来很顺手~
不巧昨天工作室中的另外一个成员说用 NSString 做键类型很业余,拼接啊、解析啊什么的,业余!!
其实我一直都觉得用字符串拼接解析这种模式还不错的,不过后来听到对方的举证,也被说服了。
对方的主要观念就用自定义的对象类型来作为字典的键类型,
我被说服的原因很简单,自定义的对象类型确实更接近面向对象的思考模式~
好,我承认我被说服了,不过因为我手头上也有 一些事情,所以并没有在这个问题上面做编码试验~
我认为cocoa 的字典应该像支持 NSString 那样,对用户自定义的键对象类型提供良好的支持。
但事实就是这么打击人,当然,最先被打击的不是我,是那个提出自定义键对象类型的伙计。
他遇到的第一个问题就是,从 NSObject 扩展出来的一个类,只包含两个int 型的属性
然后,用这个类作为字典的键的时候,竟然在 setObject: forKey:方法调用的时候,
报出 unrecognized selector "copyWithZone:" 的错误~
思忖良久外加google一番,终于发现原来是要在扩展类中将 copyWithZone 这个方法显式的重写一下~
(copyWithZone 这个方法在 NSObject 里面有所定义,报错就是因为该类型如果要作为字典的键就必须要实现这个方法)~
当然,问题如果这么轻易就被干掉了,那我也就没必要写博文记载下来了~
字典的键最基本的要求就是不能重复。。
根据我做的编码测试,copyWithZone 是将 键对象参数克隆一份,放置在字典对象里面~
但事先要做一些检查,检查新加入的键和已存在的键们是否重复,如果重复的话,就覆盖所设置的值或者忽略这一次添加~
不过怎么样,就是字典不允许存在两个一模一样的键,否则检索值的时候会发生矛盾~
检查是否重复主要集中在 NSObject 基础类的两个方法上面:
-(NSUInteger) hash;
-(BOOL) isEqual:(id)object;
hash相当于对一团数据做 md5 摘要,得出他的数字签名,大意就是两团数据只要有一丁点儿的不一样
返回的那个无符号整数的值都不会相同(那是完美的哈希算法,现实生活中只要保证很大几率下面不同的数据返回不尽相同的返回值就行了)
据我推测,检查新添加的键和已存在的键是否重复包含两步:
第一步是对比hash,如果hash都不相同的话,判定为两个键不同,将第二步省略掉;
第二部是在 hash 相等的基础上才会执行的,hash相等后,再用 isEqual 方法来判定两个键是否相等~
(此相等不是说对象的内存地址相等,而是对象中所包含的数据是否相等)~
综上,如果要自定义字典键对象的类型,要重写下面的几个方法:
1。copyWithZone:这个是必须重写的,否则直接报找不到方法的错误
2。hash:这个你可以不重写,但是你尽可以试一下,蛋疼死你~
3。isEqual: 这个方法必须要重写一下,你不重写的话,默认的实现就是对比两个对象的内存地址
只有在两个对象是同一个对象的时候,才会返回 YES。
当然这个不是我们所需要的,我们需要的就是直接构造一个新的键对象,只要这个新构造的键对象中所包含的数据
与字典中的键相一致了,就取出字典中那个键对象所对应着的值对象~
下面上代码(建一个 mac command Line Project,将下面的几个类文件拽进去),
给出一个典型的能够直接拿来作为字典键对象的类型定义:
KeyObject.h
//
// KeyObject.h
// DictionaryKeyObject
//
// Created by BruceYang on 12-7-29.
// Copyright (c) 2012年 EricGameStudio. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface KeyObject : NSObject {
int _x;
int _y;
}
@property int x;
@property int y;
+(id) kObjectWithX:(int)x y:(int)y;
-(id) initWithX:(int)x y:(int)y;
-(id) copyWithZone:(NSZone*)zone;
-(NSString*) description;
-(NSUInteger) hash;
-(BOOL) isEqual:(id)object;
@end
KeyObject.m
//
// KeyObject.m
// DictionaryKeyObject
//
// Created by BruceYang on 12-7-29.
// Copyright (c) 2012年 EricGameStudio. All rights reserved.
//
#import "KeyObject.h"
@implementation KeyObject
@synthesize x = _x;
@synthesize y = _y;
+(id) kObjectWithX:(int)x y:(int)y {
return [[[self alloc] initWithX:x y:y] autorelease];
}
-(id) initWithX:(int)x y:(int)y {
if((self = [super init])){
_x = x;
_y = y;
}
return self;
}
/**
* 注意:
* 不能 autorelease!setObject:forKey: 的时候,key 对象的 retainCount 并不会加 1~
* autorelease 的话导致一脱离所在的域,键对象就失效,引发 EXEC_BAD_ACCESS 错误~
* 用 Instruments 做 allocation 测试,可以发现键对象一直是处于存活状态的,这才是正常的情况~
*
* copyWithZone 是在 [NSMutableDictionary setObject:@"test" forKey:KeyObject对象] 方法调用的时候调用的~
* 大意就是将作为字典键的对象深拷贝一份放在字典对象里面。
* 如果 KeyObject 作为字典的键对象来使用,使用完毕后需将键对象 release 掉,因为字典并不会使用这个对象,
* 而是会克隆出一个包含相同数据的对象来~
*
* 以上也是 setObject forKey 前后,为什么键对象的 retainCount 依然为 1 的原因~
* (注:setObject forKey 前后, 值对象的 retainCount 会加 1~)
*/
-(id) copyWithZone:(NSZone*)zone {
NSLog(@"KeyObject.copyWithZone() 方法被调用~");
// 正确的写法~
return [[KeyObject alloc] initWithX:_x y:_y];
// 错误的写法~
// return [KeyObject kObjectWithX:_x y:_y];
}
-(NSString*) description {
return [NSString stringWithFormat:@"_x=%d, _y=%d", _x, _y];
}
-(NSUInteger) hash {
// NSLog(@"KeyObject.hash() 方法被调用~");
return 0;
}
-(BOOL) isEqual:(id)object {
// NSLog(@"KeyObject.isEqual() 方法被调用~");
if(![object isKindOfClass:[KeyObject class]]) {
// NSLog(@"Object passed in isn't a KeyObject instance!");
return NO;
}
KeyObject* tmp = (KeyObject*)object;
if(tmp.x == _x && tmp.y == _y) {
// NSLog(@"## Equals —— tmp.x=%d, tmp.y=%d, _x=%d, _y=%d", tmp.x, tmp.y, _x, _y);
return YES;
} else {
// NSLog(@"## Not Equals —— tmp.x=%d, tmp.y=%d, _x=%d, _y=%d", tmp.x, tmp.y, _x, _y);
return NO;
}
}
-(void) dealloc {
NSLog(@"%@%@", [self description], @" dealloc!");
[super dealloc];
}
@end
下面是测试类
KeyTest.h
//
// KeyTest.h
// DictionaryKeyObject
//
// Created by BruceYang on 12-7-31.
// Copyright (c) 2012年 EricGameStudio. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "KeyObject.h"
@interface KeyTest : NSObject {
NSMutableDictionary* _dic;
}
-(id) init;
-(void) test;
-(void) add;
-(void) add1;
-(void) echo;
-(void) echo1;
-(void) search;
-(void) dealloc;
@end
KeyTest.mm
//
// KeyTest.mm
// DictionaryKeyObject
//
// Created by BruceYang on 12-7-31.
// Copyright (c) 2012年 EricGameStudio. All rights reserved.
//
#import "KeyTest.h"
@implementation KeyTest
-(id) init {
if((self = [super init])) {
_dic = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void) test {
NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
for(int i = 0; i < 5; ++ i) {
KeyObject* keyObject = [[KeyObject alloc] initWithX:i*5 y:i*3];
NSString* valueStr = [NSString stringWithFormat:@"Test%d", i];
/**
* 在调用 setObject:forKey 方法以后,
* key 对象的引用计数依然为 1,value 对象的引用计数增加为 2~
*
*/
NSLog(@"1.kRefCount = %lu, vRefCount = %lu", [keyObject retainCount], [valueStr retainCount]); // 1, 1~
/**
* 作为参数传进来的 keyObject 不是 autorelease 的对象,所以要手动的释放掉~
* 之所以要去手动释放掉是因为字典对象已经将作为参数传进来的 keyObject 键对象深拷贝了一份~
* 这也是之前提到的在调用字典对象的 setObject:forKey 方法后 keyObject 引用计数为什么依然还是 1 的原因~
* 因为字典对象里面的键对象并不是使用的作为参数传进来的那个 keyObject ~
*/
[dic setObject:valueStr forKey:keyObject];
[keyObject release];
NSLog(@"2.KRefCount = %lu, vRefCount = %lu", [keyObject retainCount], [valueStr retainCount]); // 1, 2~
}
NSLog(@"\n---------------------- 分割线 -------------------------\n");
for(KeyObject* keyObject in dic) {
NSString* valueStr = [dic objectForKey:keyObject];
NSLog(@"ValueStrReferenceCount = %lu", [valueStr retainCount]);
NSLog(@"keyObject.x = %d, keyObject.y = %d, valueStr = %@", keyObject.x, keyObject.y, valueStr);
}
/**
* 引用计数为 1 的字典对象 release 一下之后引用计数依然为 1,何解?
* 比较合理的一种解释:
* dealloc 之后的对象去取 retainCount 会返回 1 而非 0~
*/
NSLog(@"1.dicRefCount = %lu", [dic retainCount]); // 1~
[dic release];
NSLog(@"2.dicRefCount = %lu", [dic retainCount]); // 1~
}
-(void) add {
NSLog(@"add");
for(int i = 0; i < 5; ++ i) {
/**
* 因为是 autorelease 的键对象,所以使用完毕后不用再去手动释放掉~
*/
KeyObject* keyObject = [KeyObject kObjectWithX:i*5 y:i*3];
NSString* valueStr = [NSString stringWithFormat:@"Test%d", i];
[_dic setObject:valueStr forKey:keyObject];
}
NSLog(@"_dic.count = %lu", [_dic count]);
}
-(void) add1 {
NSLog(@"add");
for(int i = 0; i < 5; ++ i) {
/**
* 非 autorelease 的键对象,使用完毕后要手动去 release 掉,效率较高(人为控制的销毁时机是最佳的)~
*/
KeyObject* keyObject = [[KeyObject alloc] initWithX:i*5 y:i*3];
NSString* valueStr = [NSString stringWithFormat:@"Test%d", i];
[_dic setObject:valueStr forKey:keyObject];
[keyObject release];
}
NSLog(@"_dic.count = %lu", [_dic count]);
}
/**
* objective-c 字典常规的遍历方式,先快速遍历字典的键,然后用键找出对应的值~
*/
-(void) echo {
NSLog(@"echo");
NSLog(@"_dic.count = %lu", [_dic count]);
for(KeyObject* keyObject in _dic) {
NSString* valueStr = [_dic objectForKey:keyObject];
NSLog(@"keyObject.x = %d, keyObject.y = %d, valueStr = %@", keyObject.x, keyObject.y, valueStr);
}
}
/**
* 朋友问我怎么打印出 NSMutableDictionary 中的内容,我不假思索地便回答:遍历+打印啊~
* 然后,对方投来鄙视的一眼,“你就不知道重写值对象的 -(NSString*) description;方法?”
* 顿时我就傻了~在 java 里面没少这么干,在 objective-c 里面竟然不知道举一反三,实乃罪过~
*/
-(void) echo1 {
NSLog(@"echo1");
NSLog(@"_dic.count = %lu", [_dic count]);
NSLog(@"%@", _dic);
}
-(void) search {
NSLog(@" -------- search begin! --------");
KeyObject* k0 = [KeyObject kObjectWithX:5 y:3];
NSString* result0 = [_dic objectForKey:k0];
NSLog(@"结果为:%@", result0);
KeyObject* k1 = [KeyObject kObjectWithX:20 y:12];
NSString* result1 = [_dic objectForKey:k1];
NSLog(@"结果为:%@", result1);
NSLog(@" -------- search finish! --------");
}
-(void) dealloc {
[_dic release];
[super dealloc];
}
@end
最后是 main.m
//
// main.m
// DictionaryKeyObject
//
// Created by BruceYang on 12-7-31.
// Copyright (c) 2012年 EricGameStudio. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "KeyTest.h"
int main (int argc, const char * argv[]) {
// 自动释放池~
@autoreleasepool {
KeyTest* kt = [[KeyTest alloc] init];
// [kt test];
[kt add1];
[kt echo1];
[kt echo];
[kt search];
}
return 0;
}
工程文件的下载地址:
上传中,稍后奉上......
http://download.csdn.net/detail/yang3wei/4465769
csdn 上传资源的功能模块有所进步,以前传资源的时候不等半个小时,新上传的资源根本就瞧不见~
分享到:
相关推荐
详情请参见我的博文: http://blog.csdn.net/yang3wei/article/details/7804171
《Objective-C程序设计》(作者杨正洪、郑齐心、李建国)通过大量的实例系统地介绍了Objective-C语言的基本概念、语法规则、框架、类库及开发环境。读者在阅读本书后,可以掌握Objective-C语言的基本内容,并进行...
Objective-C-如何创建和使用NSDictionary和NSMutableDictionary-Latest-2017-Hindi- 视频:-Yogesh Patel在Objective-C中使用NSDictionary和NSMutableDictionary的介绍。 在本频道中,我将在多部视频中讲授大多数...
是关于iOS开发语言部分Objective-C中字典对象NSDictionary的方法使用总结
Objective-C&Swift库可以轻松将NSDictionary映射到模型对象,与Alamofire完美配合。 ObjectMapper与GSON类似
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if (...
主要介绍了Objective-C中NSNumber与NSDictionary的用法简介,虽然Objective-C即将不再是iOS的主流开发语言...well,需要的朋友可以参考下
验证套件Lua 绑定Objective-C,实现了大部分Objective-C 特性待办事项支持类-ivar,您不能在方法块中通过名称(例如,_ivarName)访问 ivar 完全支持块,除了一些有线条件外,几乎完全支持块特征支持大部分Objective...
GenericModel 支持Objective-C Model、NSDictionary、JSON之间互相转换,框架非常简单高效,内部对反射过的model设置有缓存,用Objective-C中的Protocol限定NSArray,NSDictionary等容器类的类型,防止容器类型变量...
JSON Web 令牌的 Objective-c 实现。 在 iOS/MacOS 应用程序上编码和解码 JWT 的简单方法。 哈希算法 SHA256 SHA384 SHA512 应用程序接口 + (NSString *) encodeWithPayload:(NSDictionary *) andKey:...
基础语法:熟悉C语言基础是前提,Objective-C是在C语言的基础上增加了面向对象特性。 面向对象:理解类(Class)、对象(Object)、实例变量(Instance Variables)、方法(Methods)、继承(Inheritance)、封装...
Objective-C 运行时属性自省 以 NSDictionary 的形式按需打印出类的属性和值。 有关更多信息,请阅读: : 安装 是推荐的安装NSDictionary-Introspect 。 只需Podfile添加到您的Podfile : 播客文件 pod '...
Software engineering,programming language,operating system,iOS,OS,iPhone,iPad objective c,cocoa touch,SDK,object oriented design,Apple,Macintosh,tools,language,runtime,Xcode,objective-...
Objective C数组的内存布局要了解NSArray,NSSet,NSDictionary这些集合类的使法,我们需要先弄明其对应的内存布局(Memory L
NSDictionary-Enhance NSDictionary NSMutableDictionary 增强 Import pod 'NSDictionary-Enhance', '~> 1.0.0' Usage #import "NSDictionary+VDEnhance.h" [NSDictionary vd_dictionaryWithDictionary:originDic ...
自定义类在NSdictionary当key的示例, 简单,粗暴!
Objective-C的功能运算符从Swift.Sequence派生的功能运算符的Objective-C库,可帮助您编写更简洁易读的代码进行集合转换。 支持的基础集合包括: NSArray , NSDictionary , NSOrderedSet和NSSet 。功能运算符过滤...
Objective-C XML阅读器源码,源码XMLReader,XMLReader是一个基于NSXMLParser的Objective-C XML阅读器,该源码比较简单的,。This project comes from a component developed by Troy Brant and published on his ...