2009-09-10 8 views
24

Я хотел бы переопределить метод в классе Objective C, к которому у меня нет источника.Использование супер в объективной категории C?

Я изучил его, и кажется, что Категории должны позволить мне это сделать, но я хотел бы использовать результат старого метода в своем новом методе, используя super, чтобы получить результат старых методов.

Всякий раз, когда я пытаюсь это сделать, мой метод вызывается, но «супер» - это ноль ... Любая идея почему? Я занимаюсь разработкой iPhone с помощью SDK XCode 2.2. Я определенно работаю с экземпляром класса, а метод класса - это метод экземпляра.

@implementation SampleClass (filePathResolver) 
-(NSString*) fullPathFromRelativePath:(NSString*) relPath 
{ 
    NSString *result = [super fullPathFromRelativePath: relPath]; 

    ... do some stuff with the old result 

    return result; 
} 

Примечание и уточнение: Из того, что я могу видеть в Apple, Docs, то мне кажется, что это должно быть разрешено?

Categories docs at developer.apple.com: Когда категория переопределяет наследуемый метод, метод в категории может, как обычно, вызовите унаследованную реализацию через сообщение к супер. Однако, если категория переопределяет метод, который уже существовал в классе категории, там не может ссылаться на реализацию .

+0

ли вы когда-нибудь найти решение этой проблемы? Я пытаюсь изменить параметр текста, поскольку он передается в метод setText: UILabel. Я не хочу подкласса UILabel (огромная база кода), поэтому категория, скорее всего, подходит. Я тоже думаю, что вызов super setText сделал бы трюк, если супер объект был на самом деле UILabel, но тогда он скорее всего приведет к бесконечному циклу, вызывающему setText: определенному в категории. Поэтому ясно, что это, вероятно, невероятный подвиг для категории. –

+0

Эй! Я никогда не нашел другого решения, кроме того, что было опубликовано на этой странице. Используйте метод swizzling или другой метод в категории ;-( –

ответ

28

Категории продляют первоначальный класс, но они не подклассифицируют его, поэтому звонок super не находит способ.

Что вы хотите назвать Method Swizzling. Но имейте в виду, что ваш код может что-то сломать. Есть статья о Theocacao written by Scot Stevenson о методе Swizzling в старой среде выполнения Objective-C, Cocoa with Love by Matt Gallagher содержит статью о методе Swizzling в новой среде выполнения Objective-C 2.0 и простой замене для нее.

В качестве альтернативы вы можете подклассифицировать класс, а затем либо использовать подкласс, либо использовать + (void)poseAsClass:(Class)aClass для замены суперкласса. Apple, пишет:

Метод, определенный с помощью позирования класса может, через сообщение в super, включить суперкласс метода, который он переопределения.

Имейте в виду, что компания Apple уже устарела poseAsClass: в Mac OS X 10.5.

+0

Меня несколько беспокоит то, что существует концепция программирования под названием «swizzling». Однако это интересная концепция. –

+1

Как я добавил в качестве пояснения выше, что это делает из категории «apple docs»? Когда категория переопределяет унаследованный метод, метод в категории может, как обычно, вызывать унаследованную реализацию через сообщение супер. Однако, если категория переопределяет метод, который уже существует в класс категории, нет способа вызвать исходную реализацию. –

+0

Только вы действительно не хотите, чтобы метод swizzling был единственным возможным способом решения проблемы. Даже тогда, знаете, что swizzling приходит с кучей хрупкости и вопросы обслуживания. – bbum

8

Если вы будете кодирования против этого класса, просто переименуйте селектор что-то ваш код может использовать и вызвать оригинальный селектор на self:

@implementation SampleClass (filePathResolver) 
-(NSString*) myFullPathFromRelativePath:(NSString*) relPath 
{ 
    NSString *result = [self fullPathFromRelativePath: relPath]; 

    ... do some stuff with the old result 

    return result; 
} 

Если вы хотите переопределить реализацию по умолчанию это селектор для этого класса, вам нужно будет использовать подход method swizzling.

+0

Да, я не контролирую весь код, написанный против этого класса ... так что это не работает для меня, но спасибо за предложение! –

+1

^ehm, да. Метод swizzling kinda кажется огромным взломом и потенциальным будущим точкой прерывания вашей программы, но он должен работать именно в той ситуации, когда у вас нет контроля над кодом, написанным против класса. – n13

+0

хорошо, что я имел в виду, есть другой код, который я не контролирую, который вызывает «fullPathFromRelativePath» в этом классе. Я не могу изменить то, что делает другой код, поэтому swizzling мне не поможет. –

1

Не совсем в категории, но есть обходное решение путем динамического добавления метода во время выполнения.Самуэль Défago в своей статье описывает аккуратный способ создания реализации IMP Вызывающий блок супер, его оригинал статьи можно найти here

Соответствующий код:

#import <objc/runtime.h> 
#import <objc/message.h> 

    const char *types = method_getTypeEncoding(class_getInstanceMethod(clazz, selector)); 
    class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { 
     struct objc_super super = { 
      .receiver = self, 
      .super_class = class_getSuperclass(clazz) 
     }; 

     id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper; 
     return objc_msgSendSuper_typed(&super, selector, argp); 
    }), types);