ios - 遍历从属性列表中读入的 NSDictionary 证明很麻烦

标签 ios annotations mkmapview property-list

伙计们,我正在阅读一个结构如下的 plist:

property list

可以看到,它是一个包含不同类型(C,Visitor)注解信息的plist。我可以很好地显示每个注释类型,但我试图遍历所有类型并一次在 map View 上显示所有注释。这是代码:

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 但结果是一样的。因此,当我在 TableView 中选择“All Permits”行时,它会挂起几分之一秒(没有切换到 map View ),然后崩溃,但没有产生错误。

如果有人不介意在这里帮助我,我将不胜感激。提前致谢!

编辑: 这是堆栈跟踪(在下面的评论中描述):

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 循环中释放 permitDict(第二个 [permitDict release] 没有注释),所以我不知道为什么它提示 NSDictionary 是不可变的。如您所见,permitDict 是 NSMutableDictionary 类型,所以我不明白为什么它会给我这个错误。

最佳答案

代码崩溃的主要原因有两个。

首先,for循环内的这一行:

[rootOfPermitDataPlistDict release];

销毁您当前正在循环的对象。将它移到最后 - 在 if ([self title]... 语句的右大括号之后。

其次,两行说:

[permitDict release];

应该被删除。不要释放 permitDict,因为您正在使用返回自动释放对象的 dictionaryWithDictionary 创建它。

有了这两个更改,代码应该可以运行了。


但是,还有一些其他问题:

  • 您 alloc+init annotationsArray 但从未释放它(内存泄漏)。在遍历该数组的 for 循环之后释放它(您当前有第二个 [permitDict release];)。
  • 您 alloc+init annotationContainerDict 但随后仅将该变量用作对 annotationsArray 中对象的引用(因此放弃了分配的内存——内存泄漏)。不要费心在 annotationContainerDict 上做 alloc+init,只需声明它。将 NSDictionary *annotationContainerDict = [[NSDictionary alloc] init]; 更改为 NSDictionary *annotationContainerDict;
  • 您正在使用 == 比较字符串。使用 isEqualToString: 而不是像这样:
    if ([[self title] isEqualToString:@"All Permits"])...。有关为什么应该使用 isEqualToString 的解释,请参阅 this questionthis question .
  • 这里并不是一个严重的问题,但没有必要使用 dictionaryWithDictionary 并因此复制 rootOfPermitDataPlistDict 中已有的内容。您可以像这样使用 permitDict 作为对嵌入式字典的简写引用:NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];
  • 与 annotationsArray 的相同点:您不需要 alloc+init 新数组。该数组已经在 rootOfPermitDataPlistDict 中。只需像这样获取对它的引用:NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];。如果您决定这样做,请不要释放 annotationsArray。

Memory Management Programming Guide详细解释了所有这些。

关于ios - 遍历从属性列表中读入的 NSDictionary 证明很麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4281983/

相关文章:

java - 如何使用 Maven 检查缺少的 @Override 注释

java - Spring 注解扫描优化

ios - MKMapView 重置回世界 View

ios - 搜索区域在 swift MKMapView 中获取坐标

ios - 我可以使用相同的 NSString 作为 @synchronized 的一个信号量吗

ios - 运行我的第一个 iOS 应用程序时收到 SIGABRT

ios - 在 ios 应用程序上使用 facebook Logo

ios - 每当应用程序快速出现在屏幕上时如何运行功能?

sqlite - MapKit:在iPhone中缩放 map 之前不会显示注释

iphone - MKAnnotationView - 锁定自定义注释 View 以固定位置更新