通过从属性列表中读取的nsdictionary循环,证明麻烦
-
28-09-2019 - |
题
嗯,我正在从类似结构的plist读书中读书:
如您所见,它是一个包含不同类型(C,访问者)的注释信息。我可以很好地显示每个注释类型,但是我试图循环浏览所有类型,并立即在地图视图上显示所有注释。这是代码:
NSLog(@"loadAnnotations");
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"PermitData" ofType:@"plist"];
NSDictionary *rootOfPermitDataPlistDict = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
// NSMutableDictionary *permitDict = [[NSMutableDictionary alloc] init];
if ([self title] == @"All Permits") {
for (id key in rootOfPermitDataPlistDict) {
NSLog(@"key:%@",key);
//[key retain];
NSMutableDictionary *permitDict = [NSDictionary dictionaryWithDictionary:[rootOfPermitDataPlistDict objectForKey:key]];
//[key release];
//array containing annotation information: latitude, longitude, title, subtitle(see PermitData.plist)
NSArray *annotationsArray = [[NSArray alloc] initWithArray:[permitDict objectForKey:@"annotations"]];
[permitDict release];
[rootOfPermitDataPlistDict release];
CLLocationCoordinate2D workingCoordinate;
NSDictionary *annotationContainerDict = [[NSDictionary alloc] init];
//loop through annotations array, creating parking annotations filled with the information found in the plist
for(annotationContainerDict in annotationsArray){
NSLog(@"%@",annotationContainerDict);
ParkingAnnotation *parkingAnnot = [[ParkingAnnotation alloc] init];
workingCoordinate.latitude = [[annotationContainerDict objectForKey:@"latitude"] doubleValue];
workingCoordinate.longitude = [[annotationContainerDict objectForKey:@"longitude"] doubleValue];
[parkingAnnot setCoordinate:workingCoordinate];
[parkingAnnot setTitle:[annotationContainerDict objectForKey:@"title"]];
[parkingAnnot setSubtitle:[annotationContainerDict objectForKey:@"subtitle"]];
if ([parkingAnnot title] == @"C Parking") [parkingAnnot setAnnotationType:annotationTypeC];
else if ([parkingAnnot title] == @"Visitor Parking") [parkingAnnot setAnnotationType:annotationTypeVisitor];
[mapView addAnnotation:parkingAnnot];
[parkingAnnot release];
}
[permitDict release];
}
}
这是我运行程序时的控制台输出:
2010-11-25 03:25:28.020 Parking[38918:207] All Permits
2010-11-25 03:25:28.021 Parking[38918:207] loadAnnotations
2010-11-25 03:25:28.021 Parking[38918:207] key:C
2010-11-25 03:25:28.021 Parking[38918:207] {
latitude = "38.545301";
longitude = "-121.754066";
subtitle = "VP 17";
title = "C Parking";
}
2010-11-25 03:25:28.022 Parking[38918:207] {
latitude = "38.544831";
longitude = "-121.754785";
subtitle = "VP 16";
title = "C Parking";
}
2010-11-25 03:25:28.022 Parking[38918:207] {
latitude = "38.544781";
longitude = "-121.755729";
subtitle = "VP 22";
title = "C Parking";
}
2010-11-25 03:25:28.022 Parking[38918:207] {
latitude = "38.544412";
longitude = "-121.752489";
subtitle = "VP 15";
title = "C Parking";
}
因此,它可以正确地循环遍历第一个nsdictionary,但是当它即将开始循环穿过下一个时,它会崩溃。我已经尝试将Nsdictionary更改为Nsmutabledictionary,但结果是相同的。因此,当我在表视图中选择“所有允许”行时,它将悬挂在一秒钟的时间内(无需切换到地图视图),然后崩溃,而不会生成错误。
如果有人不介意在这里帮助我,我将非常感谢。提前致谢!
编辑:这是堆栈跟踪(以下评论中描述):
2010-11-25 20:28:08.141 Parking[39400:207] All Permits
2010-11-25 20:28:08.142 Parking[39400:207] loadAnnotations
2010-11-25 20:28:08.142 Parking[39400:207] key:C
2010-11-25 20:28:08.143 Parking[39400:207] {
latitude = "38.545301";
longitude = "-121.754066";
subtitle = "VP 17";
title = "C Parking";
}
2010-11-25 20:28:08.143 Parking[39400:207] {
latitude = "38.544831";
longitude = "-121.754785";
subtitle = "VP 16";
title = "C Parking";
}
2010-11-25 20:28:08.143 Parking[39400:207] {
latitude = "38.544781";
longitude = "-121.755729";
subtitle = "VP 22";
title = "C Parking";
}
2010-11-25 20:28:08.144 Parking[39400:207] {
latitude = "38.544412";
longitude = "-121.752489";
subtitle = "VP 15";
title = "C Parking";
}
2010-11-25 20:28:08.145 Parking[39400:207] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFDictionary: 0x6d5fd80> was mutated while being enumerated.<CFBasicHash 0x6d5fd80 [0x2667380]>{type = mutable dict, count = 1,
entries =>
0 : <0x7380> = <NSKeyValueContainerClass: Original class: ParkingAnnotation, Notifying class: NSKVONotifying_ParkingAnnotation>
}
'
*** Call stack at first throw:
(
0 CoreFoundation 0x025fdb99 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x0274d40e objc_exception_throw + 47
2 CoreFoundation 0x025fd659 __NSFastEnumerationMutationHandler + 377
3 Parking 0x00002e93 -[ParkingMapViewController loadAnnotations] + 364
4 Parking 0x00002caf -[ParkingMapViewController viewDidLoad] + 117
5 UIKit 0x0036a5ca -[UIViewController view] + 179
6 UIKit 0x003689f4 -[UIViewController contentScrollView] + 42
7 UIKit 0x003787e2 -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] + 48
8 UIKit 0x00376ea3 -[UINavigationController _layoutViewController:] + 43
9 UIKit 0x00378067 -[UINavigationController _startTransition:fromViewController:toViewController:] + 326
10 UIKit 0x00372ccd -[UINavigationController _startDeferredTransitionIfNeeded] + 266
11 UIKit 0x00379d8b -[UINavigationController pushViewController:transition:forceImmediate:] + 876
12 UIKit 0x00372b67 -[UINavigationController pushViewController:animated:] + 62
13 Parking 0x00002914 -[PermitListViewController tableView:didSelectRowAtIndexPath:] + 307
14 UIKit 0x00333a48 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1140
15 UIKit 0x0032a32e -[UITableView _userSelectRowAtIndexPath:] + 219
16 Foundation 0x0003f21a __NSFireDelayedPerform + 441
17 CoreFoundation 0x025def73 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
18 CoreFoundation 0x025e05b4 __CFRunLoopDoTimer + 1364
19 CoreFoundation 0x0253cdd9 __CFRunLoopRun + 1817
20 CoreFoundation 0x0253c350 CFRunLoopRunSpecific + 208
21 CoreFoundation 0x0253c271 CFRunLoopRunInMode + 97
22 GraphicsServices 0x02edc00c GSEventRunModal + 217
23 GraphicsServices 0x02edc0d1 GSEventRun + 115
24 UIKit 0x002ceaf2 UIApplicationMain + 1160
25 Parking 0x00001e08 main + 102
26 Parking 0x00001d99 start + 53
)
terminate called after throwing an instance of 'NSException'
但是,我正在释放for循环中的许可证(第二[许可证释放]未注册),所以我不知道为什么它抱怨nsdictionary是不可能的。如您所见,许可证是NSMutableDictional类型的,因此我为为什么会给我这个错误而迷失了方向。
解决方案
该代码崩溃的原因有两个。
第一的, ,这条线在前面:
[rootOfPermitDataPlistDict release];
破坏您当前正在循环的对象。将其移至末端 - 闭合的支架 if ([self title]...
陈述。
第二, ,两行说:
[permitDict release];
应删除。请勿发布许可证,因为您是使用返回自动发行对象的字典WithDictionary创建它的。
通过这两个更改,代码应运行。
然而, ,还有其他一些问题:
- 你alloc+init
annotationsArray
但切勿释放它(内存泄漏)。在通过该数组的前面释放(您当前有第二个数组)[permitDict release];
). - 你alloc+init
annotationContainerDict
但是,然后仅使用变量作为对AnnotationsArray中对象的引用(因此放弃了分配的内存 - 记忆泄漏)。不要在AnnotationContainerdict上打扰Alloc+Init,只是声明它。改变NSDictionary *annotationContainerDict = [[NSDictionary alloc] init];
只是NSDictionary *annotationContainerDict;
. - 您正在比较使用
==
. 。采用isEqualToString:
而是这样:
if ([[self title] isEqualToString:@"All Permits"])...
. 。有关为什么要使用的解释isEqualToString
, , 看 这个问题 和 这个问题. - 这里并不是一个严重的问题,但是不必使用词典Withdictionary,从而复制了rootofpermitdataplistdict中已经存在的内容。您可以将许可证用作对嵌入式词典的速记引用:
NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];
. - 与AnnotationsArray相同的一点:您不需要Alloc+Init a new Array。该数组已经在rootofpermitdataplistdict中。只需以这样的引用:
NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];
. 。如果您决定这样做,请不要发布注释。
这 内存管理编程指南 详细说明了所有这些。
其他提示
首先,您的代码有很多问题,但是除非这是绩效关键代码,否则您将每秒循环数百次(可能会创建许多自动发行的对象),使用alloc] init] init]方法只会使代码成为代码很难跟随。 (因为您必须在心理上进行并不断平衡一个释放)。不要误会我的意思,重要的是您了解保留和释放等。这就是我解决问题的方式。
请记住,您处于控制之中。让您的停车注释课程可以自己“思考”。在下面的示例中,我添加了一个 - (id)initwithDictionary:使您的其他类不必坐在那里,并做所有一个笨拙的工作,以一个一个一个一个一个一个一个设置键。 (有一些虚构的部分,我认为您会填写空白...
NSString * const PPAnnotationsKey = @"annotations";
NSString * const PPTitleKey = @"title";
NSString * const PPSubtitleKey = @"subtitle";
NSString * const PPLatitudeKey = @"latitude";
NSString * const PPLongitudeKey = @"longitude";
NSLog(@"loadAnnotations");
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"PermitData" ofType:@"plist"]; // autoreleased
NSDictionary *permitDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath]; // autoreleased
if ([[self title] isEqualToString:@"All Permits"]) {
for (NSString *parkingGroup in permitDictionary) {
NSLog(@"parkingGroup == %@", parkingGroup);
NSArray *annotations = [parkingGroup objectForKey:PPAnnotationsKey];
for (NSDictionary *entry in annotations) {
PPParkingAnnotation *annotation = [PPParkingAnnotation parkingAnnotationWithDictionary:entry]; // autoreleased
if (annotation) {
// assuming here that mapView's addAnnotation: will retain the
// annotation
[mapView addAnnotation:annotation];
}
}
}
}
@interface PPParkingAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
+ (id)parkingAnnotationWithDictionary:(NSDictionary *)dictionary;
- (id)initWithDictionary:(NSDictionary *)dictionary;
@properties...
@end
@implementation PPParkingAnnotation
+ (id)parkingAnnotationWithDictionary:(NSDictionary *)dictionary {
return [[[[self class] alloc] initWithDictionary:dictionary] autorelease];
}
- (id)initWithDictionary:(NSDictionary *)dictionary {
[self setTitle:[dictionary objectForKey:PPTitleKey]];
[self setSubtitle:[dictionary objectForKey:PPTitleKey]];
// and so on.
}