التحميل الكسول في الهدف -C - هل يجب أن أتصل بالمستقر من داخل Getter؟

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

سؤال

هذه تفاصيل صغيرة ، لكن في كل مرة أحمل فيها شيء ما أتعامل معه. هل كلتا الطريقتين مقبولين؟ هل إما أفضل؟ افترض أن المتغير لديه خاصية الاحتفاظ.

طريقة 1

(AnObject *)theObject{
    if (theObject == nil){
        theObject = [[AnObject createAnAutoreleasedObject] retain];
    }
    return theObject;
}

الطريقة رقم 2

(AnObject *)theObject{
    if (theObject == nil){
        self.theObject = [AnObject createAnAutoreleasedObject];
    }
    return theObject;
}

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

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

المحلول

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

مع #1 ، أنت تتجنب Setter ، وبالتالي ، فإن أي شيء يراقب التغيير لن يرى التغيير. من خلال "المراقبة" ، أشير بشكل خاص إلى مراقبة القيمة الرئيسية (بما في ذلك روابط الكاكاو ، والتي تستخدم KVO لتحديث واجهة المستخدم تلقائيًا).

مع #2 ، سوف تثير إشعار التغيير ، وتحديث واجهة المستخدم ، وإلا كما لو تم استدعاء Setter.

في كلتا الحالتين ، لديك إمكانية لعلامة لا حصر لها إذا استدعاء تهيئة الكائن getter. ويشمل ذلك إذا طلب أي مراقب عن القيمة القديمة كجزء من إشعار التغيير. لا تفعل ذلك.

إذا كنت ستستخدم أي من الطريقة ، فكر بعناية في العواقب. واحد لديه القدرة على ترك التطبيق في حالة غير متناسقة لأن تغيير الدولة في العقار لم يتم إخطاره والآخر لديه القدرة على طريق مسدود.

من الأفضل تجنب القضية تمامًا. انظر أدناه.


ضع في اعتبارك (مجموعة Garbage On ، أداة سطر أوامر الكاكاو القياسية:

#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
    NSString *bar;
}
@property(nonatomic, retain) NSString *bar;
@end
@implementation Foo
- (NSString *) bar
{
    if (!bar) {
        NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
        [self willChangeValueForKey: @"bar"];
        bar = @"lazy value";
        [self didChangeValueForKey: @"bar"];
    }
    return bar;
}

- (void) setBar: (NSString *) aString
{
    NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString);
    bar = aString;
}
@end

@interface Bar:NSObject
@end
@implementation Bar
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
    NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change);
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Foo *foo = [Foo new];
    Bar *observer = [Bar new];
    CFRetain(observer);
    [foo addObserver:observer forKeyPath:@"bar"
             options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew
             context:NULL];
    foo.bar;
    foo.bar = @"baz";
    CFRelease(observer);

    [pool drain];
    return 0;
}

هذا لا يتدلى. ينشر:

2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = "lazy value";
}
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = baz;
}

إذا كنت ستضيف NSKeyValueObservingOptionOld إلى قائمة الخيارات للمراقبة ، فإنه معلق إلى حد كبير.

العودة إلى تعليق أدليت به سابقًا ؛ أفضل حل هو لا تفعل التهيئة كسول كجزء من getter/setter. إنه محبوب للغاية. أنت أفضل حالًا في إدارة حالة الرسم البياني الكائن الخاص بك على مستوى أعلى ، وكجزء من ذلك ، لديك انتقال حالة هو أساسا "يو! سأستخدم هذا النظام الفرعي الآن! دافئ هذا الولد الشرير! " وهذا يفعل التهيئة كسول.

نصائح أخرى

هذه الطرق ليست متطابقة أبدًا. الأول على حق ، في حين أن الثانية هي خاطئ - ظلم - يظلم! قد لا يتصل getter أبدًا will/didChangeValueForKey: وبالتالي ليس أيضا setter. سيؤدي ذلك إلى عودة لا حصر لها إذا لوحظت تلك الخاصية.

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

إذا كنت تعرف أن طريقة Setter Property هي عبارة عن أداة محتفظ بها قياسية ، فهي متطابقة. إذا لم يكن الأمر كذلك ، فأنت بحاجة إلى تحديد ما إذا كان ينبغي استدعاء سلوك Setter الآخر أثناء العملية. إذا كنت لا تعرف ، فمن الأكثر أمانًا استخدام Setter ، لأن سلوكه قد يكون مهمًا. لا تعرقها.

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

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