سؤال

هل يمكنني اعتراض استدعاء الأسلوب في Objective-C؟كيف؟

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

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

المحلول

يمكنك القيام بذلك عن طريق استدعاء الطريقة.بافتراض أنك تريد الحصول على جميع الإصدارات إلى NSTableView:

static IMP gOriginalRelease = nil;
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) {
   NSLog(@"Release called on an NSTableView");
   gOriginalRelease(self, releaseSelector);
}


  //Then somewhere do this:
  gOriginalRelease = class_replaceMethod([NSTableView class], @selector(release), newNSTableViewRelease, "v@:");

يمكنك الحصول على مزيد من التفاصيل في وقت تشغيل Objective C توثيق.

نصائح أخرى

يتم اعتراض استدعاءات الأسلوب في Objective-C (بافتراض أنه Objective-C، وليس استدعاء C) باستخدام تقنية تسمى طريقة الأزيز.

يمكنك العثور على مقدمة حول كيفية تنفيذ ذلك هنا.على سبيل المثال، كيفية تنفيذ طريقة swizzling في مشروع حقيقي OCMock (إطار عزل للهدف-C).

يتم ترجمة إرسال رسالة في Objective-C إلى استدعاء الوظيفة objc_msgSend(receiver, selector, arguments) أو أحد متغيراته objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret.

إذا كان من الممكن تغيير تنفيذ هذه الوظائف، فيمكننا اعتراض أي رسالة.للأسف، objc_msgSend يعد جزءًا من وقت تشغيل Objective-C ولا يمكن تجاوزه.

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

لا تتضمن الورقة الأجزاء المثيرة للاهتمام من كود المصدر وليس لدي أي فكرة عما إذا كان مثل هذا النهج سيكون له آثار جانبية في الكاكاو.ولكن قد يكون من المثير للاهتمام المحاولة.

إذا كنت تريد تسجيل إرسال الرسائل من رمز التطبيق الخاص بك، فإن -forwardingTargetForSelector:النصيحة هي جزء من الحل.
لف الكائن الخاص بك:

@interface Interceptor : NSObject
@property (nonatomic, retain) id interceptedTarget;
@end

@implementation Interceptor
@synthesize interceptedTarget=_interceptedTarget;

- (void)dealloc {
    [_interceptedTarget release];
    [super dealloc];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"Intercepting %@", NSStringFromSelector(aSelector));
    return self.interceptedTarget;
} 

@end

الآن افعل شيئًا كهذا:

Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;

وسيكون لديك سجل لإرسال الرسائل.لاحظ أنه لن يتم اعتراض عمليات الإرسال المرسلة من داخل الكائن الذي تم اعتراضه، حيث سيتم إرسالها باستخدام المؤشر "الذاتي" للكائن الأصلي.

إذا كنت تريد فقط تسجيل الرسائل التي يتم الاتصال بها من الخارج (عادةً ما يتم الاتصال بها من المندوبين؛لمعرفة نوع الرسائل ومتى وما إلى ذلك)، يمكنك تجاوز ResponseToSelector مثل هذا:

- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(@"respondsToSelector called for '%@'", NSStringFromSelector(aSelector));

// look up, if a method is implemented
if([[self class] instancesRespondToSelector:aSelector]) return YES;

return NO;

}

إنشاء فئة فرعية من NSProxy وتنفيذها -forwardInvocation: و -methodSignatureForSelector: (أو -forwardingTargetForSelector:, ، إذا كنت تقوم ببساطة بتوجيهه إلى كائن آخر بدلاً من العبث بالطريقة بنفسك).

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

إذا لم ينجح هذا الأمر بالنسبة لك، فقدم تفاصيل إضافية حول موقفك.

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

يمكنك خلط استدعاء الأسلوب مع واحد خاص بك، والذي يفعل ما تريد القيام به عند "الاعتراض" ويستدعي التنفيذ الأصلي.تتم عملية Swizzling باستخدام class_replaceMethod().

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

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

ولكن إذا كنت تقصد بكلمة "اعتراض" "توقف"، فربما تحتاج إلى مزيد من المنطق لتقرر ما إذا كان ينبغي استدعاء الطريقة على الإطلاق.

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