سؤال

لدي تطبيق يعمل في الخلفية فقط (عن طريق تحديد LSBackgroundOnly في ملف info.plist). المشكلة هي أن جميع الكتل التي أقوم بتشغيلها في قوائم الانتظار المتوازية لا يتم إطلاقها. يتم تنفيذ الكود في بيئة تديرها الذاكرة - لا يوجد GC.

الرمز (المبسط) يبدو أدناه. Blubber هو مجرد فئة وهمية تحمل nsdate للاختبار. أيضا ، يكتب retain, release, ، و dealloc للقيام ببعض قطع الأشجار:

NSOperationQueue *concurrentQueue = [[NSOperationQueue alloc] init];
[concurrentQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];

Blubber *aBlubber = [[Blubber alloc] init]; 
aBlubber.aDate = [NSDate date];

[concurrentQueue addOperationWithBlock:^{       
NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
    NSDate *test = [aBlubber aDate];
    NSLog(@"Block DONE");
    [blockPool release];    
}];

[aBlubber release];

[concurrentQueue release];

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

هل هناك أي طريقة لإجبار Runloop يدويًا أو أي شيء مسؤول عن إخبار قوائم الانتظار بإصدار الكتل النهائية؟

(لا يتم إصدار جميع الكائنات الأخرى التي تم الاحتفاظ بها بواسطة الكتل ، مما يخلق تسربًا ضخمًا للذاكرة. لا يمكن أن تكون هذه التسريبات من خلال التسريبات أو أدوات ObjectAllocation ولكن يمكن ملاحظة استهلاك الذاكرة في ارتفاع باستخدام Top.)

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

المحلول

أحد "Gotcha" الشائع لسباحة Autorelease هو أنه إذا كان التطبيق يبني الذاكرة دون تلقي الأحداث ، أقصى الخارجي لن يستنزف البلياردو (الذي تديره حلقة الحدث).

لا أعتقد أن هذا يجب أن ينطبق هنا لأنك تدير حمام السباحة الخاص بك ... ولكن فقط في حالة ، يمكنك تجربة هذا:

...
//When no events are coming in (i.e. the user is away from their computer), the runloop doesn't iterate, and we accumulate autoreleased objects
[[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:@selector(kickRunLoop:) userInfo:nil repeats:YES] retain];
...
- (void) kickRunLoop:(NSTimer *)dummy
{
// Send a fake event to wake the loop up.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
                                    location:NSMakePoint(0,0)
                               modifierFlags:0
                                   timestamp:0
                                windowNumber:0
                                     context:NULL
                                     subtype:0
                                       data1:0
                                       data2:0]
         atStart:NO];
}

نصائح أخرى

يبدو أنك تستخدم كتلة تعتمد على المكدس التي يتم استخدامها بعد انتهاء الكتلة. يجب نسخ الكتلة. يجب أن يعمل الرمز إذا تم تغييره إلى هذا:

[concurrentQueue addOperationWithBlock:[[^{       
    NSAutoreleasePool *blockPool = [[NSAutoreleasePool alloc] init];
    NSDate *test = [aBlubber aDate];
    NSLog(@"Block DONE");
    [blockPool release];    
}copy]autorelease]];

ألقِ نظرة على هذا المنشور للحصول على كتابة كاملة على الكتل: http://gkoreman.com/blog/2011/02/27/blocks-in-c-objective-c/

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top