objective c - ماهي - هل من الممكن جعل-init طريقة خاصة في Objective-C؟




لغة c (6)

NS_UNAVAILABLE

- (instancetype)init NS_UNAVAILABLE;

هذا هو الإصدار القصير للسمة غير المتاحة. ظهر لأول مرة في MacOS 10.7 و iOS 5 . يتم تعريف في NSObjCRuntime.h كـ #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE .

يوجد إصدار يعطل الطريقة فقط لعملاء Swift ، وليس لشفرة ObjC:

- (instancetype)init NS_SWIFT_UNAVAILABLE;

unavailable

أضف السمة unavailable إلى الرأس لإنشاء خطأ في المحول البرمجي في أي مكالمة إلى init.

-(instancetype) init __attribute__((unavailable("init not available")));  

إذا لم يكن لديك سبب ، فقط اكتب __attribute__((unavailable)) ، أو حتى __unavailable :

-(instancetype) __unavailable init;  

doesNotRecognizeSelector:

استخدم doesNotRecognizeSelector: لرفع NSInvalidArgumentException. "يقوم نظام وقت التشغيل باستدعاء هذه الطريقة عندما يتلقى كائن رسالة aSelector لا يمكنه الاستجابة لها أو إعادة توجيهها."

- (instancetype) init {
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

NSAssert

استخدم NSAssert لرمي NSInternalInconsistencyException وعرض رسالة:

- (instancetype) init {
    [self release];
    NSAssert(false,@"unavailable, use initWithBlah: instead");
    return nil;
}

raise:format:

استخدام raise:format: لرمي الاستثناء الخاص بك:

- (instancetype) init {
    [self release];
    [NSException raise:NSGenericException 
                format:@"Disabled. Use +[[%@ alloc] %@] instead",
                       NSStringFromClass([self class]),
                       NSStringFromSelector(@selector(initWithStateDictionary:))];
    return nil;
}

هناك حاجة [self release] لأن الكائن تم alloc بالفعل. عند استخدام ARC ، سيقوم المترجم بالاتصال به نيابة عنك. في أي حال ، لا داعي للقلق عندما تكون على وشك إيقاف التنفيذ عن قصد.

objc_designated_initializer

إذا كنت تعتزم تعطيل init لفرض استخدام مُهيئ معين ، فهناك سمة لذلك:

-(instancetype)myOwnInit NS_DESIGNATED_INITIALIZER;

هذا ينشئ تحذير ما إلا باستدعاء myOwnInit داخليًا أي أسلوب تهيئة آخر. سيتم نشر التفاصيل في Adopting Modern Objective-C بعد إصدار Xcode التالي (على ما أظن).

أحتاج إلى إخفاء (جعل الخصوصية) طريقة -init من صفي في Objective-C.

كيف أقوم بذلك؟


إذا كنت تتحدث عن طريقة -Init الافتراضية فلا يمكنك ذلك. إنه موروث من NSObject وكل صف سوف يستجيب له بدون تحذيرات.

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

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


ضع هذا في ملف الرأس

- (id)init UNAVAILABLE_ATTRIBUTE;

كذلك المشكلة لماذا لا يمكنك جعله "خاص / غير مرئي" هو السبب يحصل على الأسلوب init يرسل إلى المعرف (كما يعيد تخصيص معرف) لا إلى YourClass

لاحظ أنه من نقطة المترجم (المدقق) ، يمكن لمعرف أن يستجيب لأي شيء تم كتابته (لا يمكنه التحقق من حقيقة ما هو معرف في وقت التشغيل) ، بحيث يمكنك إخفاء init فقط عندما لا يوجد شيء في أي مكان (علانية = في رأس) استخدم طريقة init ، من أن الترجمة ستعرف ، أنه لا توجد وسيلة لاستجابة id ، لأنه لا يوجد init في أي مكان (في المصدر الخاص بك ، جميع libs الخ ...)

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

ببساطة عن طريق تنفيذ init ، الذي يعيد الصفر ويكون له مُهيئ (خاص / غير مرئي) ، والذي لن يحصل على شخص آخر (مثل initOnce ، initWithSpecial ...)

static SomeClass * SInstance = nil;

- (id)init
{
    // possibly throw smth. here
    return nil;
}

- (id)initOnce
{
    self = [super init];
    if (self) {
        return self;
    }
    return nil;
}

+ (SomeClass *) shared 
{
    if (nil == SInstance) {
        SInstance = [[SomeClass alloc] initOnce];
    }
    return SInstance;
}

ملاحظة: يمكن لشخص ما القيام بذلك

SomeClass * c = [[SomeClass alloc] initOnce];

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

يمكن أن نمنع هذا إلى أبعد من ذلك ، لكن ليس هناك حاجة


يجب أن أذكر أن وضع التأكيدات وإثارة الاستثناءات لإخفاء الأساليب في الفئة الفرعية له فخ فخ للقاعدة.

أوصي باستخدام __unavailable كما شرح Jano __unavailable الأول .

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

- (SuperClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    ...bla bla...
    return self;
}

- (SuperClass *)initWithLessParameters:(Type1 *)arg1
{
    self = [self initWithParameters:arg1 optional:DEFAULT_ARG2];
    return self;
}

تخيل ما يحدث لـ -InitWithLessParameters ، إذا قمت بذلك في الفئة الفرعية:

- (SubClass *)initWithParameters:(Type1 *)arg1 optional:(Type2 *)arg2
{
    [self release];
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

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

كما يعني أيضًا أنه يمكنك استخدام التأكيدات والاستثناءات في الطرق التي يجب تجاوزها في الفئات الفرعية. (أساليب "مجردة" كما في إنشاء فئة مجردة في Objective-C )

ولا تنس طريقة + الفصل الجديد.


يعتمد ذلك على ما تعنيه بـ "اجعله خاصًا". في Objective-C ، قد يكون من الأفضل وصف طريقة استدعاء أحد الكائنات على أنها إرسال رسالة إلى ذلك الكائن. لا يوجد شيء في اللغة يمنع العميل من استدعاء أي طريقة معينة على كائن ما ؛ أفضل ما يمكنك القيام به هو عدم الإعلان عن الطريقة في ملف الرأس. إذا كان العميل على الرغم من ذلك يطلق على الأسلوب "الخاص" مع التوقيع الصحيح ، فإنه لا يزال ينفذ في وقت التشغيل.

ومع ذلك ، فإن الطريقة الأكثر شيوعًا لإنشاء طريقة خاصة في Objective-C هي إنشاء Category في ملف التطبيق ، والإعلان عن جميع الطرق "المخفية" هناك. تذكر أن هذا لن يمنع المكالمات من بدء التشغيل ، ولكن المحلل سيبث التحذيرات إذا حاول أي شخص القيام بذلك.

MyClass.m

@interface MyClass (PrivateMethods)
- (NSString*) init;
@end

@implementation MyClass

- (NSString*) init
{
    // code...
}

@end

هناك thread لائق على MacRumors.com حول هذا الموضوع.





objective-c