Objective-C forwardInvocation: - PullRequest
       34

Objective-C forwardInvocation:

4 голосов
/ 07 июля 2011

Я часто делаю что-то вроде:

CoolViewController *coolViewController = [[CoolViewController alloc] init];
[self.navigationController pushViewController:coolViewController animated:YES];
[coolViewController release];

Как бы я в категории UINavigationController переопределил forwardInvocation:, чтобы я мог вместо этого сделать:

[self.navigationController pushCoolViewControllerAnimated:YES];
  1. Пожалуйста, включите соответствующий код в ваш ответ, а не просто объяснение. Спасибо!

  2. Не стесняйтесь комментировать, является ли это хорошей практикой. Я также спрашиваю об этом в образовательных целях, но мне кажется, что в этом случае упрощение кода может перевесить незаметную (правильную?) Стоимость времени обработки и использования памяти. Кроме того, я родом из Ruby и очень люблю использовать динамическое программирование для упрощения вещей, например, динамические искатели (например, find_by_name) в Rails.

  3. Бонусные баллы, если вы могли бы реализовать pushCoolViewControllerAnimated:withBlock и вызвать блок после инициализации контроллера представления, что позволило мне установить определенные переменные экземпляра на созданном контроллере представления.

ОБНОВЛЕНИЕ: Я только что вспомнил, что ARC скоро появится. Таким образом, этот конкретный пример может быть не очень полезным, но все же это отличное упражнение / пример, который можно использовать в других случаях, например, для динамических искателей для базовых данных и передачи блока для настройки NSFetchRequest.

1 Ответ

14 голосов
/ 07 июля 2011

Используйте механизм разрешения динамического метода, описанный в Руководстве по программированию 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 для включения вызова исходной реализации решит эту проблему.

Версия, принимающая блок после создания, является простым расширением (измените параметры блока, измените кодировку типа, измените шаблон имени селектора и способ извлечения предполагаемого имени класса).

...