سؤال

أردت الحصول على شيء موضح.

دعنا نقول أن لدي الرمز التالي:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

سيؤدي ذلك إلى إنشاء 5 ملايين سلاسل تم تصنيفها في ضمن هذه الدعوة. كنت أتوقع أن يحافظ هذا على هذه الكائنات حتى إنهاء التطبيق ، حيث أن AutoreleAsepool الوحيد الذي أراه هو التغليف لاستئصال التطبيق في Main.M. هذا ليس هو الحال ، رغم ذلك. في نهاية مكالمة الوظيفة هذه ، يبدو أنهم جميعًا يحصلون على إصدارهم ويتم إزالتهم من الذاكرة.

هذا المستند:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsautoreleasepool_class/reference.html

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

هذا أمر منطقي بالنسبة لي ، لكن هذا تحت Uikit ، وليس طقم التطبيق. سؤالي هو ، هل يفعل Uikit/Cocoa Touch نفس الشيء في هذه الحالة ، أم أن هناك تفسيرًا آخر لإطلاق الأشياء الخاصة بي؟

شكرًا!

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

المحلول 2

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

تحرير: توفر إجابة روب بعض المعلومات الإضافية الجيدة المتعلقة بالسلوك تحت ARC. بشكل عام ، من العدل أن نقول إن الأشياء أقل عرضة في نهاية المطاف في تجمع Autorelease نظرًا لبعض التحسينات التي يمكن أن تقوم بها قوس.

نصائح أخرى

أجاب أندرو على سؤالك الرئيسي بأنه ، نعم ، سيتم استنزاف حمام السباحة التلقائي في كل دورة من حلقة التشغيل الرئيسية. لذلك أي كائنات autorelease تم إنشاؤها في viewDidLoad قد يتم استنزافها على الفور عندما تعود إلى حلقة التشغيل الرئيسية. من المؤكد أنها لن يتم الاحتفاظ بها "حتى إنهاء التطبيق".

لكن يجب أن نكون حذرين: فأنت تفترض بوضوح أنه تتم إضافة هذه الكائنات إلى تجمع Autorelease. بعض التحذيرات لهذا الافتراض:

  1. في الماضي (وما زال مطلوبًا لتواصل التشغيل ARC-MRC) ، عند إرجاع الكائنات من الطرق التي لم تبدأ أسماؤها alloc, new, copy, ، أو mutableCopy, ، ستؤدي هذه الكائنات إلى الحصول على الكائنات التلقائية ، والتي يتم تعاملها فقط عند استنزاف تجمع Autorelease (أي عندما عادت إلى حلقة التشغيل).

  2. لكن Arc أصبح أكثر ذكاءً حول التقليل من الحاجة إلى برك Autorelease (انظر http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease, الذي يناقش callerAcceptsFastAutorelease, ، ينادى الآن callerAcceptsOptimizedReturn استدعى من قبل prepareOptimizedReturn) ، لذلك غالبًا ما لن ترى هذا autorelease سلوك. لذلك ، إذا كان كل من المكتبة والمتصل يستخدمان ARC ، فقد لا يتم وضع الكائنات في تجمع Autorelease ، ولكن Arc ستصدرها بذكاء على الفور إذا لم تكن هناك حاجة إليها.

    مع مشاريع ARC المعاصرة ، لا يلزم عمومًا تجمعات Autorelease. لكن بعض الحالات الخاصة ، لا يزال بإمكان المرء الاستفادة من استخدام برك Autorelease. سأوضح إحدى تلك الحالات أدناه.

النظر في الرمز التالي:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

سيضيف الرمز التالي 500000 كائن إلى تجمع Autorelease ، والذي سيتم تجفيفه فقط عندما أعود إلى حلقة التشغيل:

no pool

في هذه الحالة ، يمكنك استخدام تجمع Autorelease لتقليل علامة المياه العالية:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

pool

خلاصة القول ، مع ARC ، ليس من الواضح دائمًا عندما يستخدم كائنًا تلقائيًا وعندما يطلقه بشكل صريح عندما يسقط المتغير خارج النطاق. يمكنك دائمًا تأكيد ذلك من خلال فحص السلوك في الأدوات.

جانبا ، سأكون حذرا من رسم الكثير من استنتاجات إدارة الذاكرة العامة عند استخدام NSString الفصل ، لأنه تم تحسينه بشكل كبير ولا يتوافق دائمًا مع ممارسات إدارة الذاكرة القياسية.

أفترض أنه عندما تقوم بتعيين كائن جديد إلى مرجع يستخدم لعقد كائن بعد ذلك يتم إصدار الكائن الأصلي على الفور (إذا لم يكن هناك شيء آخر يشير إليه - يذهب عدد المرجع إلى الصفر) باستخدام ARC و Asuming الافتراضي strong الرجوع كما في مثال الحلقة الخاصة بك.

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

تلاحظ أبل في الانتقال إلى ملاحظات إصدار القوس

حاول التوقف عن التفكير في مكان وضع مكالمات الاحتفاظ/الإصدار والتفكير في خوارزميات التطبيق بدلاً من ذلك. فكر في مؤشرات "قوية وضعيفة" في الأشياء الخاصة بك ، حول ملكية الكائنات ، وحول دورات الاحتفاظ المحتملة.

يبدو أن release يتم استدعاؤه على الكائن عند تعيين قيمة جديدة ، من Clang Clang 3.4 الوثائق الهدف-C العد المرجعي التلقائي (ARC)

تحدث المهمة عند تقييم مشغل المهمة. تختلف الدلالات بناءً على المؤهل:

بالنسبة للكائنات __strong ، يتم الاحتفاظ بالبويضة الجديدة لأول مرة ؛ ثانياً ، يتم تحميل LVALUE مع دلالات بدائية. ثالثًا ، يتم تخزين Pointee الجديد في Lvalue مع دلالات بدائية ؛ وأخيراً ، يتم إطلاق Pointee القديم. هذا لم يتم تنفيذه ذريًا ؛ يجب استخدام المزامنة الخارجية لجعل هذا آمنًا في مواجهة الأحمال والمتاجر المتزامنة.

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