苹果是否在iOS 5中阻止了Method swizling ?
我玩了一下,发现一个带有Method Swizzling的应用程序可以在iOS 4上运行,但不能在iOS 5上运行。
苹果不久前向一些被发现在App Store应用程序中使用方法swizling的开发者发送了一封电子邮件:
你的应用程序,xxx,当前发布到应用程序商店正在使用method_exchangeImplementations交换Apple的实现提供带有您自己实现的api。因为即将到来更改时,此行为可能会导致应用程序崩溃或导致iPhone OS 4.0的用户数据丢失。
xxx使用method_exchangeImplementations交换实现你的方法ttdealloc。它还交换了方法popViewControllerAnimated的实现方法popViewControllerAnimated2:。
请立即解决此问题并将新的二进制文件上传到iTunes连接。如果我们相信这一点,我们可能会删除你的申请这样做是谨慎的或必要的。
看起来他们想要摆脱它,所以我认为他们现在很有可能已经完全封锁了它。
UPDATE:(我的应用程序使用此方法并且在appstore中)
方法混合似乎在2012年5月30日起起作用。这是我的实现
(这是为那些你四处寻找,发现wiki页面上的坏代码,只是想要一个快速实现。)
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上的reuseIdentifier与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) load在应用程序启动时被调用一次,这是实现方法swizzle的好地方。不幸的是,在一些开发情况下+(void)load没有被调用,你可能需要测试你的应用程序,以确保这些方法被调用
我们在一个月前(2012年5月初)收到了一款应用的批准,该应用大量使用Swizzling方法来定制iOS4中的标准UI组件(iOS5使用外观)。此外,method swizzling是一个完全文档化的API,它还提供了与Apple本身或使用私有API无关的非常强大的功能。我很难相信他们会拒绝这样的事情!
无论如何,如果你看到更多与此相关的拒绝,请通知每个人!谢谢!