Ey guys, I am reading in from a plist structured like so:
As you can see, it is a plist that contains annotation information of different types(C,Visitor). I can display each annotation type just fine, but I am attempting to loop through all types and display all annotations on the map view at once. Here is the code:
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];
}
}
And this is the console output when I run the program:
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";
}
So it loops through the first NSDictionary properly, but crashes when it is about to begin looping through the next one. And I have tried changing NSDictionary to NSMutableDictionary but the result is the same. So when I select the "All Permits" row in the table view, it hangs for a fraction of a second(without switching to the map view), and then crashes, without generating an error.
If anyone wouldn't mind helping me out here, I would greatly appreciate it. Thanks in advance!
EDIT: Here is the stack trace(described in comments below):
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'
However, I am releasing permitDict within the for loop(second [permitDict release] was left uncommented), so I don't know why it is complaining about the NSDictionary being immutable. As you can see, the permitDict is of type NSMutableDictionary, so I am lost as to why it is giving me this error.
The code is crashing for two main reasons.
First, this line which is inside the for-loop:
[rootOfPermitDataPlistDict release];
destroys the very object that you're currently looping through. Move it to the very end--after the closing brace of the if ([self title]...
statement.
Second, the two lines that say:
[permitDict release];
should be removed. Do not release permitDict because you are creating it using dictionaryWithDictionary which returns an autoreleased object.
With those two changes, the code should run.
annotationsArray
but never release it (memory leak). Release it after the for-loop that goes through that array (where you currently have the second [permitDict release];
).annotationContainerDict
but then use the variable only as a reference to the objects in the annotationsArray (and so abandoning the allocated memory--memory leak). Don't bother doing alloc+init on annotationContainerDict, just declare it. Change NSDictionary *annotationContainerDict = [[NSDictionary alloc] init];
to just NSDictionary *annotationContainerDict;
.==
. Use isEqualToString:
instead like this:if ([[self title] isEqualToString:@"All Permits"])...
. For an explanation of why you should use isEqualToString
, see this question and this question.NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];
.NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];
. Do not release annotationsArray if you decide to do it this way.The Memory Management Programming Guide explains all this in detail.