#objective-c #objective-c-runtime
#objective-c #objective-c-время выполнения
Вопрос:
Я играю с внедрением метода, но обжигаю пальцы на этом.
Есть два класса, cats и dogs. Собаки пытаются разгадать секрет кошек, используя speak
метод. Собаки выясняют, что у кошек есть catSecret
метод, но не могут динамически вводить ему замену. Почему?
Соответствующие методы на Dog
стороне
- ( NSString * ) speak
{
return @"Woof";
}
- ( NSString * ) wannabeCat
{
return @"Meow?";
}
( BOOL ) resolveInstanceMethod:( SEL ) aSelector
{
NSString * s = NSStringFromSelector ( aSelector );
if ( [s containsString:@"Secret"] )
{
NSLog ( @"Oops - the cats have a %@", s );
IMP m = [Dog.class methodForSelector:@selector ( wannabeCat )];
class_addMethod ( Dog.class, aSelector, m, "@@:" );
NSLog ( @"tAdded" );
return YES;
}
else
{
NSLog ( @"Skipping %@", s );
return [super resolveInstanceMethod:aSelector];
}
}
Редактировать
Ранее я пытался сделать то же самое в forwardingTargetForSelector
методе, но после комментариев от Cy-4AH я переместил его в resolveInstanceMethod
то место, которое кажется подходящим для этого.
Собаки надеялись, что после этого у Dog
класса будет добавлен метод, но это не работает. Он по-прежнему завершается '-[Dog catSecret]: unrecognized selector sent to instance
сообщением. Мой вопрос — почему это не работает?
На Cat
стороне это соответствующие фрагменты.
- ( NSString * ) catSecret
{
return @"scratch";
}
- ( NSString * ) speak
{
return [NSString stringWithFormat:@"Miaau (%@)", self.catSecret]
}
Я надеялся динамически внедрить catSecret
селектор с wannabeCat
реализацией на Dog
стороне.
Результат выглядит следующим образом
2020-10-28 12:05:12.613324 0200 Swizzle[4786:119045] Oops - the cats have a catSecret
2020-10-28 12:05:12.613357 0200 Swizzle[4786:119045] Added
2020-10-28 12:05:12.613434 0200 Swizzle[4786:119045] Skipping _forwardStackInvocation:
2020-10-28 12:05:12.613482 0200 Swizzle[4786:119045] -[Dog catSecret]: unrecognized selector sent to instance 0x1006b2730
Комментарии:
1.
forwardingTargetForSelector
для пересылки цели для селектора, это значит найти кого-то, кто может выполнить селектор. Для динамического добавления реализации для селекторов вам нужноresolveInstanceMethod:
2. Да, я надеялся внедрить метод
forwardingTargetForSelector
, поэтому я возвращаюсьself
оттуда… но я попробую это сделатьresolveInstanceMethod:
…3. Попробуйте поместить свой тестовый код внутрь
dyspatch_asynch
, возможно, вы пытаетесь протестировать его на ранней стадии4. Я переместил код
resolveInstanceMethod:
, и затем он добавляет его без проблем. Я думаю, что это способ сделать это, спасибо, но это все равно заканчивается нераспознанным исключением селектора. Я регистрирую селекторы, которые он хочет разрешить, а также получаю a_forwardStackInvocation:
, сresolveInstanceMethod
которым я не знаю, как обращаться. Я полагаю, я просто ищу способ динамического внедрения, а затем использования метода, и то, что я пытаюсь, не работает. Примеры, которые я видел, обычно вводят какой-то известный метод, но здесь. метод обнаруживается динамически.
Ответ №1:
Редактировать
Очищено на основе комментариев Cy-4AH.
Первым, гораздо большим шагом было перенести внедрение метода resolveInstanceMethod
в, и я отредактировал вопрос, чтобы отразить это. Это также было связано с Cy-4AH, который к настоящему времени я могу почти печатать без ошибок.
Однако окончательное исправление было небольшим … то, как код находится в вопросе
IMP m = [Dog.class methodForSelector:@selector ( wannabeCat )];
относится к методу класса, а не к методу экземпляра. Это должно быть изменено на
IMP m = class_getMethodImplementation ( Dog.class, @selector ( wannabeCat ) );
чтобы сделать его методом экземпляра, и тогда он работает просто отлично, к радости собак…
Для справки, полный новый метод становится
( BOOL ) resolveInstanceMethod:( SEL ) aSelector
{
if ( [super resolveInstanceMethod:aSelector] )
{
return YES;
}
else
{
NSLog ( @"Oops - the cats have a %@", NSStringFromSelector( aSelector ) );
IMP m = class_getMethodImplementation ( Dog.class, @selector ( wannabeCat ) );
class_addMethod ( Dog.class, aSelector, m, "@@:" );
NSLog ( @"tAdded" );
return YES;
}
}
Комментарии:
1. Вам нужно
class_getMethodImplementation(self, @selector ( wannabeCat ))
вместо[[Dog alloc] init...
2. Да! Намного чище, спасибо… Я отредактировал его в ответ.