حلقة من خلال nsdictionary قراءة من قائمة الممتلكات proving مزعجة

StackOverflow https://stackoverflow.com/questions/4281983

سؤال

يا شباب ، أنا أقرأ من plist منظم مثل ذلك:

property list

كما ترون ، فهو عبارة عن 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 غير قابلة للتغيير. كما ترون ، فإن التصريح من النوع nsmutabletichary ، لذلك أنا ضائع حول سبب إعطائي هذا الخطأ.

هل كانت مفيدة؟

المحلول

الكود ينهار لسببين رئيسيين.

أولاً, ، هذا الخط الموجود داخل الحلقة:

[rootOfPermitDataPlistDict release];

يدمر الكائن الذي تحلقه حاليًا. انقلها إلى النهاية-بعد الدعامة الختامية لـ if ([self title]... بيان.

ثانيا, ، الخطان اللذان يقولان:

[permitDict release];

يجب ازلته. لا تقم بإصدار تصريحك لأنك تقوم بإنشائها باستخدام DictionaryWithDictionary الذي يعيد كائنًا تلقائيًا.

مع هذين التغييرين ، يجب تشغيل الكود.


لكن, ، هناك بعض المشاكل الأخرى:

  • أنت تخصيص+init annotationsArray ولكن لا تطلقه أبدًا (تسرب الذاكرة). حرره بعد الحلقة التي تمر عبر تلك الصفيف (حيث لديك حاليًا للثاني [permitDict release];).
  • أنت تخصيص+init annotationContainerDict ولكن بعد ذلك ، استخدم المتغير فقط كمرجع إلى الكائنات في التعليقات التوضيحية (وهكذا التخلي عن الذاكرة المخصصة-تسرب الذاكرة). لا تهتم بالقيام بتخصيص+init على التعليق التوضيحي ، فقط أعلن ذلك. يتغيرون NSDictionary *annotationContainerDict = [[NSDictionary alloc] init]; لمجرد NSDictionary *annotationContainerDict;.
  • أنت تقارن الأوتار باستخدام ==. يستخدم isEqualToString: بدلا من ذلك مثل هذا:
    if ([[self title] isEqualToString:@"All Permits"]).... للحصول على شرح لسبب استخدامك isEqualToString, ، نرى هذا السؤال و هذا السؤال.
  • ليست مشكلة خطيرة حقًا هنا ، لكن من غير الضروري استخدام DictionaryWithdictionary وبالتالي تكرار ما هو موجود بالفعل في RootofPermitDatapListDict. يمكنك فقط استخدام Commondict كمرجع اختصار إلى القاموس المدمج مثل هذا: NSMutableDictionary *permitDict = [rootOfPermitDataPlistDict objectForKey:key];.
  • نفس النقطة مع التعليقات التوضيحية: لا تحتاج إلى تخصيص صفيف جديد. الصفيف موجود بالفعل في rootofpermitdataplistdict. فقط احصل على إشارة إليها مثل هذا: NSArray *annotationsArray = [permitDict objectForKey:@"annotations"];. لا تصدر التعليقات التوضيحية إذا قررت القيام بذلك بهذه الطريقة.

ال دليل برمجة إدارة الذاكرة يشرح كل هذا بالتفصيل.

نصائح أخرى

أولاً ، كانت هناك العديد من المشكلات في الكود الخاص بك ، ولكن ما لم يكن هذا هو رمز حرجة للأداء ، حيث ستحلق مئات المرات في الثانية (والتي يمكن أن تنشئ العديد من الكائنات التي تم تصنيفها) ، باستخدام طريقة init] من الصعب متابعة. (نظرًا لأنه سيتعين عليك المرور عقلياً وتوازن مستمر مع تخصيص مع إصدار). لا تفهموني خطأ ، من المهم أن تفهم الاحتفاظ والإفراج ، وما إلى ذلك. لكن هذه هي الطريقة التي سأقترب بها من المشكلة.

تذكر أنك في السيطرة. اجعل فصول شرح وقوف السيارات قادرة على "التفكير بأنفسهم" قليلاً. في المثال أدناه ، أضفت initwithdictionary (ID): بحيث لا يتعين على فصلك الآخر الجلوس هناك والقيام بكل أعمال Grunt المتمثلة في تعيين المفاتيح واحدًا تلو الآخر. (هناك بعض الأجزاء الخيالية حيث أفترض أنك ستملأ الفراغات ...

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.
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top