Захватывающие Цель C классы - PullRequest
3 голосов
/ 17 мая 2011

Я пытаюсь перехватить / переопределить определенные методы, которые вызываются внутри объектов, к которым у меня нет доступа.Я хотел бы иметь возможность создавать подклассы объектов, которые создаются в системных объектах.

Например, когда я создаю экземпляр UIWebView, он внутренне создает экземпляр UIScrollView.Я хотел бы иметь возможность создавать подклассы этого UIScrollView, чтобы я мог изменить некоторые аспекты его поведения путем создания подклассов.Возможно ли это?

Я пытался сделать это, используя категорию @implementation UIScrollView (MyScrollView), затем переопределяя + alloc и возвращая свой собственный объект, который является подклассом UIScrollView.Однако я сталкиваюсь с некоторыми проблемами.В принципе, я не могу достичь оригинального метода [UIScrollView alloc].Возможно, я мог бы реализовать свой собственный метод + alloc, который бы повторял поведение [NSObject alloc]?

Есть ли более простой способ сделать это?Что не так с моим текущим подходом?

Наконец, пройдет ли процесс проверки Apple?Я не называю ничего недокументированным.

Ответы [ 5 ]

4 голосов
/ 17 мая 2011

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

Для достижения таких (простите за мой французский) грязных трюковВы можете попробовать MethodSwizzling .В крайнем случае, это иногда имеет большую ценность.В прошлом я использовал его в основном для отладки, а также для реверс-инжиниринга.

Что касается вашего вопроса о процессе одобрения Apple, я бы предположил, что ваши шансы попасть в беду довольно высоки при изменении реализации по умолчанию дляUIWebView таким радикальным образом.

2 голосов
/ 17 мая 2011

@ Подход Тилля к Методу Swizzling заключается в том, как вы этого добиваетесь. У Майка Эша есть одна из лучших статей о том, как это сделать. В настоящее время я предпочитаю его метод прямого переопределения, хотя я немного переработал его следующим образом:

#import <objc/runtime.h>

@interface NSObject (RNSwizzle)
+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP;
@end

@implementation NSObject (RNSwizzle)

+ (IMP)swizzleSelector:(SEL)origSelector withIMP:(IMP)newIMP {
  Class class = [self class];
  Method origMethod = class_getInstanceMethod(class, origSelector);
  IMP origIMP = method_getImplementation(origMethod);

  if(!class_addMethod(self, origSelector, newIMP,
                      method_getTypeEncoding(origMethod)))
  {
    method_setImplementation(origMethod, newIMP);
  }

  return origIMP;
}

@end

Учитывая его пример, вы бы использовали мой метод следующим образом:

gOrigDrawRect = [UIView swizzleSelector:@selector(drawRect:)
                                withIMP:OverrideDrawRect];

Это все документированные вызовы, которые не используют частные API Apple. Apple иногда отклоняет приложения, которые вносят слишком существенные изменения в ожидаемое поведение пользовательского интерфейса (независимо от того, как они это делают), но я доставил материал с довольно существенными скрытыми изменениями пользовательского интерфейса без проблем, и это не зависит от частного API-интерфейсы.

Это не значит, что метод Swizzling стабилен. Это хрупкая техника, которая делает вас более зависимыми от недокументированных внутренних органов. Это может означать, что вам придется больше карабкаться, если Apple что-то изменит.

(Глубокое дыхание)

Есть еще один подход, который вы можете использовать здесь, если вам не нужно хранить ivar. Вы можете получить представление прокрутки и класс переместиться в ваш собственный подкласс. Я упоминал, что у вас не должно быть собственного хранилища ивара? Ошибки, которые вы создадите, если выделите в этом ивару и умопомрачительные. Дасти хорошо пишет об этом в своем блоге . (Именно по его коду я добавил ивар и столкнулся с невероятно изумительными ошибками, упомянутыми выше.)

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

2 голосов
/ 17 мая 2011

Не используйте категорию, чтобы заглушить существующие методы. Так лежит безумие.

В вашей ситуации я бы заменил представление прокрутки на экземпляр вашего пользовательского подкласса прокрутки, манипулируя иерархией представления, а не механизмом выделения.

0 голосов
/ 18 мая 2011

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

По сути, я использую категорию для перехвата вызовов + alloc моего нужного класса. Затем я реализую свой собственный метод + alloc, который просто выделяет и возвращает объект моего подкласса типа. Затем я могу переопределить любой метод в моем подклассе объекта, и они будут правильно вызваны из UIWebView. Это автоматический механизм создания подклассов: каждый экземпляр целевого класса (в данном случае UIScrollView) автоматически получает подклассы, даже если кто-то его создает.

Вот как выглядит мой метод + alloc:

+ (id) alloc
{
    return class_createInstance([MyUIScrollView class], 0);
}

Затем я реализую простой класс MyUIScrollView, который наследуется от UIScrollView и позволяет мне переопределять любые методы, которые мне могут понадобиться, что дает мне гораздо больший контроль над поведением родительского UIWebView. Круто!

Пара уловов:

  • Если в вашем приложении более одного UIScrollViews, вам нужно быть осторожным, потому что каждый из них теперь по сути станет MyUIScrollView! Решение состоит в том, чтобы в MyUIScrollView был установлен флаг, который гарантирует, что он не используется (в основном просто перенаправляет все в суперкласс) в тех случаях, когда экземпляр не принадлежит вашему конкретному UIWebView. Это легко сделать путем создания подкласса UIWebView и переопределения его методов иерархии представления (addSubview: и т. Д.), Чтобы после создания экземпляра UIWebView и добавления его внутреннего UIScrollView к иерархии представления вы могли установить флаг в своем экземпляре с подклассами. чтобы сказать, чтобы делать то, что вы хотите сделать.

  • Это работает только для приложений, которые не наследуют целевой класс (UIScrollView и т. Д.) Где-либо в приложении! Например, я успешно могу втиснуть себя в UIScrollView UIWebView, используя эту технику, и переопределить любые методы, которые я хотел бы использовать в нем, но если какой-то другой компонент в моих подклассах приложения UIScrollView (включая мой собственный код), я столкнусь с проблема, потому что я по сути отключаю все подклассы для этого класса. Все UIScrollViews в приложении будут иметь тип MyUIScrollView!

Итог: Это не рекомендуемый подход, но он выглядит как самый чистый способ решения этой проблемы.

0 голосов
/ 17 мая 2011

Я на самом деле не сделал этого, поэтому я не уверен на 100%, но я верю, что вы можете делать то, что вы пытаетесь сделать, но я не думаю, что вы можете сделать это с вашим текущим подходом , Я считаю, что вам придется использовать среду выполнения Objective C для перехвата методов. Если вы еще этого не сделали, прочитайте Справочник по Objective-C Runtime .

ОК, это банка , а что насчет должно вам и вам сойдет с рук?

Все, что вы делаете, делая это, вероятно, будет очень хрупким. Любые будущие изменения, которые Apple внесет в эти фреймворки, могут нарушить ваш код перехвата. Так что лично я подал бы это в «очень сомнительный» вопрос о том, должен ли рассмотреть вопрос о выпуске продукта, основанного на этом.

Что касается , вам это сойдет с рук? Очевидно, я не могу говорить за Apple, но моя ставка - нет. Вы правы в том, что, строго говоря, вы не вызываете недокументированный код, но вы вмешиваетесь в функционирование частных методов, поэтому вы, безусловно, противоречите духу условий App Store.

Конечно, все это только мое мнение, возьми из него, что хочешь.

...