Метод Swizzling в iOS 5?
Apple заблокировала метод Swizzling в iOS 5?
Я немного поиграл и обнаружил, что приложение с методом Swizzling работает на iOS 4, но не на iOS 5.
ПРИМЕЧАНИЕ. Приложение работает на iOS 5, но не на части, когда используется метод Swizzling.
Ответы
Ответ 1
Apple отправила электронное письмо некоторое время назад некоторым разработчикам, которые, как было установлено, используют метод swizzling в приложениях App Store:
Ваше приложение, xxx, в настоящее время отправленное в App Store, использует method_exchangeImplementations для обмена реализацией Apple предоставили API с вашими собственными реализациями. Из-за предстоящих изменения, это поведение в вашей заявке может вызвать сбой или вызвать потеря пользовательских данных на iPhone OS 4.0.
xxx использует метод_exchangeImplementations для обмена реализацией dealloc с вашим методом ttdealloc. Он также реализация метода popViewControllerAnimated: с помощью метод popViewControllerAnimated2:.
Пожалуйста, немедленно устраните эту проблему и загрузите новый iTunes Connect. Мы можем удалить вашу заявку, если считаем, что делать это разумно или необходимо.
Похоже, они хотели избавиться от него, поэтому я бы сказал, что шансы довольно высоки, и теперь они полностью заблокировали его.
Ответ 2
UPDATE: (Мое приложение использует этот метод и находится в appstore)
Метод swizzling, похоже, работает с 30 мая 2012 года. Это моя реализация.
(Это для тех из вас, кто оглядывается и находит плохой код на страницах вики и просто хочет быстро реализовать.)
Swizz.h
#import <Foundation/Foundation.h>
void ActivateAutoSwizz();
void Swizz(Class c, SEL orig, SEL replace);
@interface NSObject (swizz)
// This Method allows the class to Swizzle more methods within itself.
// And allows for an overridable init method in Class Extensions
// ######################
// //// To Swizzle a method, call Swizzle once on the class in question.
// //// dispatch_once is a good way to handle that.
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
// });
- (void) swizzInit;
@end
Swizz.m
#import "Swizz.h"
#import <objc/runtime.h>
#import <objc/message.h>
//....
void Swizz(Class c, SEL orig, SEL replace)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, replace);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, replace, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
@implementation NSObject (swizz)
// Load gets called on every object that has it. Off Thread, before application start.
+ (void) load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Swizz([NSObject class], @selector(init), @selector(initIntercept));
});
}
- (id) initIntercept{
self = [self initIntercept]; // Calls the Original init method
if(self){
[self swizzInit];
}
return self;
}
- (void) swizzInit{
//Do Nothing.. Gives an extension point for other classes.
}
@end
Я построил это, чтобы позволить мне перехватить идентификатор повторного использования в UITableViewCell с расширением UITableViewCell.
Вот этот пример.
UITableViewCell + ReuseIdentifier.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@interface UITableViewCell (ReuseIdentifier)
@end
UITableViewCell + ReuseIdentifier.m
#import "UITableViewCell+ReuseIdentifier.h"
#import "Swizz.h"
@implementation UITableViewCell(ReuseIdentifier)
// Edited to remove Class variable.
// Class variables in Categories are Global.
// And as it turns out, this method did not need the variable anyhow.
- (NSString *)classReuseIdentifier{
NSString *reuseIdentifierResult = [self classReuseIdentifier]; // Gets the original reuseIdentifier
if (reuseIdentifierResult == nil){
reuseIdentifierResult = [[self class] description];
}
return reuseIdentifierResult;
}
// Alternatively you can use the +(void)load method on the class to achieve the same thing.
- (void)swizzInit{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Swizz([UITableViewCell class], @selector(reuseIdentifier), @selector(classReuseIdentifier));
});
}
@end
Как вы можете видеть, ActivateAutoSwizz(), а также мой метод swizzInit используют dispatch_once для выполнения swizzle один раз.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Swizz([NSObject class], @selector(init), @selector(initIntercept));
});
Если вы выполните его дважды. он меняет ваш метод обратно на оригинал.
Надеюсь, это поможет некоторым из вас разработчиков iOS.
ПРИМЕЧАНИЕ. Я определил, что загрузка + (void) вызывается один раз при запуске приложения и является прекрасным местом для достижения метода swizzle. К сожалению, в некоторых ситуациях dev + (void) загрузка не вызывается, вы можете протестировать свое приложение, чтобы убедиться, что эти методы вызывают.
Ответ 3
Хорошо, мы получили OK о mont hago (начало мая 2012 года) для приложения, которое сильно использовало метод Swizzling для настройки стандартных компонентов пользовательского интерфейса в iOS4 (iOS5 с использованием внешнего вида). Кроме того, метод swizzling - полностью документированный API, который также предоставляет очень мощные функции, не связанные с самим Apple или использованием частных API. Мне трудно поверить, что они могут отказаться от такой вещи!
В любом случае, пожалуйста, сообщите всем, если вы увидите больше отклонений, связанных с этим! Спасибо!