方法在iOS 5中晃荡?

Jas*_*ani 14 iphone objective-c ios

苹果在iOS 5中阻止了方法调整吗?

我正在做一些游戏并发现一个使用Method Swizzling的应用程序可以在iOS 4上运行但在iOS 5上运行.

注意:该应用程序适用于iOS 5,但不适用于使用Method Swizzling时的部分.

Jor*_*ith 15

Apple不久前发送了一封电子邮件给一些发现在App Store应用程序中使用方法调配的开发人员:

您当前发布到App Store的应用程序xxx正在使用method_exchangeImplementations将Apple提供的API的实现与您自己的实现交换.由于即将发生的更改,应用程序中的此行为可能会导致崩溃或导致iPhone OS 4.0上的用户数据丢失.

xxx使用method_exchangeImplementations与您的方法ttdealloc交换dealloc的实现.它还用你的方法popViewControllerAnimated2:交换方法popViewControllerAnimated:的实现.

请立即解决此问题,并将新二进制文件上传到iTunes Connect.如果我们认为这样做是谨慎或必要的,我们可能会删除您的申请.

看起来他们想要摆脱它,所以我说机会很高,他们现在已经完全阻止了它.

  • @Yar好问题,我不确定答案.(虽然方法调整看起来像是非常hacky代码,如果它不能解决API限制 - 你确定没有更好的方法来构建你的代码吗?)不应该太难以测试自己,如果你发布结果在这里我我会更新答案:) (2认同)
  • @Yar哦,对不起,关联对象如`objc_setAssociatedObject`,`objc_getAssociatedObject`和`objc_removeAssociatedObjects`.运行时可以将一个对象链接到另一个低级别的对象,然后您可以编写一个setter和getter来平滑裂缝.关于前者,请参阅http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html. (2认同)

The*_*der 9

更新 :(我的应用程序使用此方法,并在appstore中)

截至2012年5月30日,方法调配似乎正在运行.这是我的实现.

(这是为了那些环顾四周并在维基页面上找到错误代码并且只是希望快速实现的人.)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我构建了这个允许我使用UITableViewCell扩展拦截UITableViewCell上的reuseIdentifier.

这是一个例子.

的UITableViewCell + ReuseIdentifier.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface UITableViewCell (ReuseIdentifier)

@end
Run Code Online (Sandbox Code Playgroud)

的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
Run Code Online (Sandbox Code Playgroud)

如您所见,ActivateAutoSwizz()以及我的swizzInit方法都使用dispatch_once来执行一次swizzle.

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    Swizz([NSObject class], @selector(init), @selector(initIntercept));
});
Run Code Online (Sandbox Code Playgroud)

如果你执行两次.它会将您的方法切换回原始状态.我希望这可以帮助你们中的一些iOS开发者.

注意:我已确定在应用程序启动时调用一次+(void)加载,这是实现方法调配的好地方.不幸的是,在某些开发情况下,未调用+(void)加载,您可能需要测试您的应用程序以确保调用这些方法.