لا يتم إصدار كتلة الهدف-C لتطبيقات الخلفية فقط
سؤال
لدي تطبيق يعمل في الخلفية فقط (عن طريق تحديد 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/