4

В настоящее время экспериментирует со способом swizzling в Objective-C, и у меня есть вопрос. Я пытаюсь понять правильный способ метода swizzle, и после изучения онлайн я наткнулся на эту запись NSHipster: http://nshipster.com/method-swizzling/Правильный способ использования swizzling в объективе-C

В статье автор имеет некоторый метод swizzling sample code. Я ищу кого-то, чтобы лучше объяснить мне, что делает автор. В частности, я смущен логикой didAddMethod. Почему автор не просто непосредственно реализует методы swapping/exchanging? Моя единственная теория на этом, возможно, есть шанс, что viewWillAppear: не добавлен в UIViewController's methoddispatch_table. В частности, если, возможно, категория загружается в память до UIViewController ... Это причина? Кажется довольно странным? Просто найдите еще более проницательность/ясность, спасибо :)

ответ

6

В частности, я смущен по логике didAddMethod. Почему автор не просто напрямую меняет/заменяет реализации методов?

Ваше замешательство понятно, поскольку эта логика не объясняется четко.

Сначала проигнорируйте тот факт, что этот пример является категорией конкретного класса UIViewController и просто рассмотрим логику, как если бы категория была на каком-то произвольном классе, назовем этот класс TargetClass.

Мы будем называть существующий метод, который мы хотим заменить existingMethod.

Категория, находясь на TargetClass, добавляет метод swizzling, который мы будем называть swizzlingMethod, до TargetClass.

Важно: Обратите внимание, что функция, чтобы получить метод, class_getInstanceMethod, найдет метод в комплект поставки класса или любого из его суперкласса. Однако функции class_addMethod и class_replaceMethod только добавляют/заменяют методы в поставляемом классе.

В настоящее время существует два случая:

  1. TargetClass сами непосредственно содержит реализацию existingMethod. Это простой случай, все, что нужно сделать, это обмен реализациями existingMethod и swizzlingMethod, что может быть сделано с помощью method_exchangeImplementations. В статье вызывается звонок class_addMethod, так как есть уже и existingMethodнепосредственно вTargetClass и логика приводит к вызову method_exchangeImplementations.

  2. TargetClass делает не непосредственно содержат реализацию existingMethod, а этот метод обеспечивается посредством наследования от одного из предков классов TargetClass. Это сложнее. Если вы просто обмениваетесь реализациями existingMethod и swizzlingMethod, то вы будете выполнять (экземпляры) класса предков (и таким образом, который может вызвать сбой - почему это остается как упражнение). Позвонив по номеру class_addMethod, код статьи гарантирует, что в TargetClass есть existingMethod, реализация которого является исходной реализацией swizzlingMethod. Логика затем заменяет реализацию swizzlingMethod на реализацию предка (что не влияет на предка).

Еще здесь? Надеюсь, это имеет смысл и не просто послало вам перекрестие!

Другое упражнение, если вы неизлечимо любопытно: Теперь вы можете спросить, что происходит, если existingMethod реализация предка содержит вызов super ... если реализация в настоящее время также прилагается к swizzlingMethod в TargetClass, где будет этот призыв к super концу вверх? Будет ли это реализация в предке, которая увидит, что реализация одного и того же метода выполняется дважды или предка предка, как первоначально предполагалось?

HTH

+0

Просто удивительный ответ, все это имеет смысл. Ваши предложения по упражнениям также велики, и я буду разбираться в них :) Вы, кажется, очень хорошо осведомлены о времени выполнения, я опубликовал еще один вопрос только сейчас. Было бы здорово, если бы у вас была возможность взглянуть (если у вас есть время). Большое спасибо! http://stackoverflow.com/questions/34503563/method-swizzling-with-method-setimplementation – AyBayBay

0

load вызывается, когда в obj-c добавляется class.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/clm/NSObject/load

Так скажем, если UIViewController добавляется в Obj-C выполнения, который уже содержит viewWillAppear:, но вы хотите его заменить другим вариантом осуществления. Поэтому сначала вы добавляете новый метод xxxWillAppear:. Теперь, когда xxxWillAppear: был добавлен в класс ViewController, только тогда вы можете его заменить.

Но автор также сказал:

Например, предположим, что мы хотели, чтобы отслеживать, сколько раз каждый контроллер представления представлен пользователю в приложение IOS

поэтому он пытается чтобы продемонстрировать случай, когда приложение может иметь множество контроллеров представлений, но вы не хотите продолжать замену для каждой реализации ViewControllerviewWillAppear:. После того, как точка viewWillAppear: была заменена, вместо этого нужно будет выполнить только обмен.

Возможно, исходный код Objective выполнения C может помочь:

/********************************************************************** 
* addMethod 
* fixme 
* Locking: runtimeLock must be held by the caller 
**********************************************************************/ 
static IMP 
addMethod(Class cls, SEL name, IMP imp, const char *types, BOOL replace) 
{ 
IMP result = nil; 

rwlock_assert_writing(&runtimeLock); 

assert(types); 
assert(cls->isRealized()); 

method_t *m; 
if ((m = getMethodNoSuper_nolock(cls, name))) { 
    // already exists 
    if (!replace) { 
     result = _method_getImplementation(m); 
    } else { 
     result = _method_setImplementation(cls, m, imp); 
    } 
} else { 
    // fixme optimize 
    method_list_t *newlist; 
    newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1); 
    newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list; 
    newlist->count = 1; 
    newlist->first.name = name; 
    newlist->first.types = strdup(types); 
    if (!ignoreSelector(name)) { 
     newlist->first.imp = imp; 
    } else { 
     newlist->first.imp = (IMP)&_objc_ignored_method; 
    } 

    attachMethodLists(cls, &newlist, 1, NO, NO, YES); 

    result = nil; 
} 

return result; 
} 


BOOL 
class_addMethod(Class cls, SEL name, IMP imp, const char *types) 
{ 
if (!cls) return NO; 

rwlock_write(&runtimeLock); 
IMP old = addMethod(cls, name, imp, types ?: "", NO); 
rwlock_unlock_write(&runtimeLock); 
return old ? NO : YES; 
} 


IMP 
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 
{ 
if (!cls) return nil; 

rwlock_write(&runtimeLock); 
IMP old = addMethod(cls, name, imp, types ?: "", YES); 
rwlock_unlock_write(&runtimeLock); 
return old; 
} 

Вы можете вырыть больше, если вы хотите:

http://www.opensource.apple.com/source/objc4/objc4-437/

Смежные вопросы