Domanda

ragazzi Ey, sto leggendo in da un plist strutturata in questo modo:

lista di proprietà

Come si può vedere, si tratta di un plist che contiene le informazioni di annotazione di diversi tipi (C, Visitatore). Posso visualizzare ogni tipo di annotazione bene, ma sto cercando di scorrere tutti i tipi e di visualizzare tutte le annotazioni sulla visualizzazione della mappa in una sola volta. Ecco il codice:

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];
  }
 }

E questo è l'output della console quando faccio funzionare il programma:

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";
}

Così scorre la prima NSDictionary correttamente, ma si blocca quando si è in procinto di iniziare il ciclo attraverso il prossimo. E ho provato a cambiare NSDictionary al NSMutableDictionary ma il risultato è lo stesso. Così quando seleziono riga "tutte le autorizzazioni" nella vista tabella, si blocca per una frazione di secondo (senza passare alla visualizzazione della mappa), e quindi si blocca, senza generare un errore.

Se qualcuno non dispiacerebbe avermi aiutato qui, notevolmente lo apprezzerei. Grazie in anticipo!

EDIT: Ecco la traccia dello stack (descritto nei commenti qui sotto):

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'

Tuttavia, mi sto liberando permitDict all'interno del ciclo for (secondo [rilascio permitDict] è stato lasciato senza commenti), quindi non so il motivo per cui si lamenta l'NSDictionary essere immutabile. Come si può vedere, la permitDict è di tipo NSMutableDictionary, quindi mi sono perso per spiegare perché mi sta dando questo errore.

È stato utile?

Soluzione

Il codice si blocca per due motivi principali.

Prima , questa linea che è all'interno del ciclo for:

[rootOfPermitDataPlistDict release];

distrugge l'oggetto stesso che si sta scorrendo. Spostarlo fino alla fine -. Dopo la parentesi graffa della dichiarazione if ([self title]... di chiusura

Secondo , le due linee che dicono:

[permitDict release];

deve essere rimosso. Non rilasciare permitDict perché si sta creando utilizzando dictionaryWithDictionary che restituisce un oggetto autoreleased.

Con queste due modifiche, il codice dovrebbe essere eseguito.


Tuttavia , ci sono un paio di altri problemi:

  • alloc + init annotationsArray ma mai rilasciate (perdita di memoria). Rilasciarlo dopo il per-ciclo che passa attraverso tale matrice (in cui è attualmente il secondo [permitDict release];).
  • alloc + init annotationContainerDict ma poi utilizzare la variabile solo come riferimento agli oggetti in annotationsArray (e quindi abbandonando la memoria allocata - perdita di memoria). Non preoccupatevi di fare alloc + init su annotationContainerDict, basta dichiararlo. Cambiare NSDictionary *annotationContainerDict = [[NSDictionary alloc] init]; a poco NSDictionary *annotationContainerDict;.
  • Si sta confrontando stringhe utilizzando ==. Utilizzare isEqualToString: invece in questo modo:
    if ([[self title] isEqualToString:@"All Permits"]).... Per una spiegazione del motivo per cui si dovrebbe usare isEqualToString, vedi questa domanda e questa domanda .
  • Non è davvero un problema serio qui, ma non è necessario utilizzare dictionaryWithDictionary e duplicando così quello che è già lì in rootOfPermitDataPlistDict. Si potrebbe utilizzare permitDict come riferimento scorciatoia al dizionario incorporato in questo modo:. NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];
  • stesso punto con annotationsArray: non c'è bisogno di alloc + init un nuovo array. L'array è già in rootOfPermitDataPlistDict. Basta avere un riferimento ad esso in questo modo: NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];. Non rilasciare annotationsArray se si decide di fare in questo modo.

Il Memory Management Guida di programmazione spiega tutto questo in dettaglio.

Altri suggerimenti

In primo luogo, ci sono stati molti problemi con il tuo codice, ma a meno che non si tratta di prestazioni di codice critica, dove sarete loop centinaia di volte al secondo (che potrebbero creare molti oggetti autoreleased), utilizzando alloc] init metodo] appena lo farà rendere il codice più difficile da seguire. (Dal momento che si dovrà passare attraverso mentalmente e bilanciare un alloc con un rilascio continuo). Non fraintendetemi, è importante comprendere trattenere e rilasciare, ecc, ma questo è il modo vorrei affrontare il problema.

Ricordati che sei in controllo. Fai la tua classi parcheggio di annotazione in grado di "pensare con la propria" un po '. Nell'esempio che segue, ho aggiunto un - (id) initWithDictionary: in modo che la vostra altra classe non deve stare lì e fare tutto il lavoro sporco di impostare i tasti uno per uno. (Ci sono alcune parti di fantasia dove mi piacerebbe pensare che ci si riempire gli spazi vuoti ...

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.
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top