Objective-C forwardInvocation:
Я часто делаю что-то вроде:
CoolViewController *coolViewController = [[CoolViewController alloc] init];
[self.navigationController pushViewController:coolViewController animated:YES];
[coolViewController release];
Как бы я, в категории UINavigationController
переопределить forwardInvocation:
так что я мог бы вместо этого сделать:
[self.navigationController pushCoolViewControllerAnimated:YES];
Пожалуйста, включите соответствующий код в ваш ответ, а не просто объяснение. Спасибо!
Не стесняйтесь комментировать, является ли это хорошей практикой. Я также спрашиваю об этом в образовательных целях, но мне кажется, что в этом случае упрощение кода может перевесить незаметную (правильную?) Стоимость времени обработки и использования памяти. Кроме того, я родом из Ruby и люблю использовать динамическое программирование для упрощения вещей, например, динамические искатели (например,
find_by_name
) в Rails.Бонусные баллы, если вы могли бы реализовать
pushCoolViewControllerAnimated:withBlock
и вызвать блок после инициализации контроллера представления, что позволяет мне устанавливать определенные переменные экземпляра на созданном контроллере представления.
ОБНОВЛЕНИЕ: Я только что вспомнил, что ARC скоро придет. Таким образом, этот конкретный пример может быть не очень полезным, но все же это отличное упражнение / пример, который можно использовать в других случаях, например, для динамических искателей для базовых данных и передачи блока для настройки NSFetchRequest
,
1 ответ
Используйте механизм разрешения динамического метода, описанный в Руководстве по программированию Objective-C, в частности, +[NSObject resolveInstanceMethod:]
:
@implementation UINavigationController (FWD)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *name = NSStringFromSelector(sel);
NSString *prefix = @"push";
NSString *suffix = @"Animated:";
if ([name hasPrefix:prefix] && [name hasSuffix:suffix]) {
NSRange classNameRange = {[prefix length],
[name length] - [prefix length] - [suffix length]}
NSString *className = [name substringWithRange:classNameRange];
Class cls = NSClassFromString(className);
if (cls) {
IMP imp = imp_implementationWithBlock(
^(id me, BOOL animated) {
id vc = [[cls alloc] init];
[me pushViewController:vc animated:animated];
[vc release];
});
class_addMethod(cls, sel, imp, "v@:c");
return YES;
}
}
return [super resolveInstanceMethod:sel];
}
@end
Конечно, если UINavigationController
уже использует +resolveInstanceMethod:
, ты сейчас сломал это. Делать это в подклассе UINavigationController
или использование метода swizzling для вызова исходной реализации решит эту проблему.
Версия, принимающая блок после создания, является прямым расширением (измените параметры блока, измените кодировку типа, измените шаблон имени селектора и способ извлечения предполагаемого имени класса).