Цикл через NSDictionary, прочитанный из списка свойств, доказывающий беспокойство - PullRequest
0 голосов
/ 26 ноября 2010

Эй, ребята, я читаю из списка, составленного так:

property list

Как видите, это лист, который содержит информацию аннотаций разных типов (C, Visitor). Я могу отображать каждый тип аннотации очень хорошо, но я пытаюсь перебрать все типы и сразу отобразить все аннотации в виде карты. Вот код:

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, но результат тот же. Поэтому, когда я выбираю строку «Все разрешения» в табличном представлении, она зависает на долю секунды (без переключения в представление карты), а затем вылетает, не генерируя ошибку.

Если кто-то не против помочь мне здесь, я был бы очень признателен. Заранее спасибо!

EDIT: Вот трассировка стека (описанная в комментариях ниже):

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'

Однако я выпускаю allowDict в цикле for (второй [releaseDict release] был оставлен без комментариев), поэтому я не знаю, почему он жалуется на то, что NSDictionary является неизменным. Как вы можете видеть, allowDict имеет тип NSMutableDictionary, поэтому я не понимаю, почему он дает мне эту ошибку.

Ответы [ 2 ]

4 голосов
/ 26 ноября 2010

Код сбой по двум основным причинам.

Первая , эта строка внутри цикла for:

[rootOfPermitDataPlistDict release];

уничтожает сам объект, которыйВы в настоящее время проходите.Переместите его в самый конец - после закрывающей скобки оператора if ([self title]....

Second , две строки, которые говорят:

[permitDict release];

должны бытьудален.Не выпускайте allowDict, потому что вы создаете его с использованием dictionaryWithDictionary, который возвращает автоматически выпущенный объект.

С этими двумя изменениями должен выполняться код. Однако , есть несколько других проблем:

  • Вы выделяете + init annotationsArray, но никогда не освобождаете его (утечка памяти).Отпустите его после цикла for, который проходит через этот массив (где у вас есть второй [permitDict release];).
  • Вы выделяете + init annotationContainerDict, но затем используете переменную только как ссылку на объекты вмассив аннотаций (и, следовательно, отказ от выделенной памяти - утечка памяти).Не пытайтесь использовать alloc + init для annotationContainerDict, просто объявите это.Измените NSDictionary *annotationContainerDict = [[NSDictionary alloc] init]; на NSDictionary *annotationContainerDict;.
  • Вы сравниваете строки, используя ==.Вместо этого используйте isEqualToString:, например:
    if ([[self title] isEqualToString:@"All Permits"])....Для объяснения того, почему вы должны использовать isEqualToString, см. этот вопрос и этот вопрос .
  • Не очень серьезная проблема здесь, но нет необходимости использовать dictionaryWithDictionaryи, таким образом, дублирование того, что уже есть в rootOfPermitDataPlistDict.Вы можете просто использовать allowDict в качестве краткой ссылки на встроенный словарь, например: NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];.
  • То же самое с annotationsArray: вам не нужно выделять + инициализировать новый массив.Массив уже находится в rootOfPermitDataPlistDict.Просто получите ссылку на это так: NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];.Не выпускайте annotationsArray, если вы решите сделать это следующим образом.

Руководство по программированию управления памятью *1053* объясняет все это подробно.

1 голос
/ 26 ноября 2010

Во-первых, с вашим кодом было много проблем, но если это не критичный к производительности код, когда вы будете циклически повторяться сотни раз в секунду (что может потенциально создать много автоматически выпущенных объектов), использование метода alloc] 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.
}
...