2011-06-24 44 views
2

由于某些原因,我必须保留原始代码而不做任何修改,但是尝试将系统API重定向到我的代码中,然后回调到原始代码。例如,我想在[NSString stringWithFormat:]中做更多的事情。是否有可能在Objective-C中重定向系统API?

现在我尝试使用方法swizzling。但似乎NSString未在主运行时加载,我将调整方法移动到MyAppDelegate。之后class_getInstanceMethod([NSString class], @selector(stringWithFormat:))不是零。但是,调整方法仍然不起作用,因为class_getInstanceMethod([NSString class], @selector(override_stringWithFormat:))仍然是零,我该如何解决这个问题?

感谢, CLU

@interface NSString (MyNSString) 

+ (id)stringWithFormat:(NSString *)format, ...; 
@end 


@implementation NSString (MyNSString) 
+ (id)stringWithFormat:(NSString *)format, ... { 
    //do something... 
    [NSString stringWithFormat:format]; 
} 
@end 

这里是MyAppDelegate

#import "MyNSString.h" 
-(void) MethodSwizzle:(Class)c replaceOrig:(SEL) origSEL withNew:(SEL) overrideSEL { 
    Method origMethod = class_getInstanceMethod(c, origSEL); 
    Method overrideMethod = class_getInstanceMethod(c, overrideSEL); 

    if(class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) 
    class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); 
else 
    method_exchangeImplementations(origMethod, overrideMethod);  
} 

- (BOOL) application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary *) options { 
    ... 
    //Unit test 
    NSString *a=[NSString override_stringWithFormat:@"Test"]; //returned something 
    Method b = class_getInstanceMethod([NSString class], @selector(override_stringWithFormat:)); //return nil; 

    //do something... 
    [self MethodSwizzle:[NSString class] replaceOrig:@selector(stringWithFormat:) withNew:@selector(override_stringWithFormat:)]; 

} 

回答

2

代码不知道你想做什么,这个问题有点不清楚。另外,不要试图从NSString继承,它是类集群的一部分,所以只需重写一个方法就会复杂得多。你甚至必须在我mplement the storage yourself。快速获得复杂的真实。

有两种方法可以将方法添加到Objective-C中的现有类:Categoriesmethod Swizzling

类别允许您在现有类上定义新方法,不可以可靠使用类别替换方法。例如:

@interface NSString (MyStuff) 
- (id)mySpecialInit; 
@end; 

@implementation NSString (MyStuff) 
/* implementation of -(id)mySpecialInit would go here */ 
@end 

此外,此方法不能可靠地替换类上的现有方法。

为此,您需要方法调整。警告 - 这是运行时的一项高级功能,让您自己进入调试噩梦非常容易,所以请谨慎使用。 Swizzling允许您用另一种具有相同签名的方法替换一种方法的实现。使用一些技巧,你也可以调用超级实现。 Matt Gallagher通常被认为拥有best implementation of method swizzling,因为它允许可靠地调用预先存在的实现(称为超级实现)。看看有关Cocoa with Love的链接文章,例如用法。您编辑的代码可能不起作用,因为您尝试调整类方法,但使用getInstanceMethod函数。改为使用class_getClassMethod()。此外,您需要重命名您的类别stringWithFormat方法,它不得与冲突的真正的方法名称。最后一个警告 - 我从来没有见过使用可变参数(...)的方法调用方法,this C q&a使我相信这是不可能的(尝试this post作为可能的解决方案)。作为一个学术活动,这里是它会可能样子:

的NSString + MyMethods.h

@interface NSString (MyMethods) 
+ (id)clu_stringWithFormat:(NSString *)format,...; 
@end 

的NSString + MyMethods.m:

@implementation NSString (MyMethods) 
+ (id)clu_stringWithFormat:(NSString *)format,... { 
    //Do your stuff here 
    //call supersequent implementation 
    //again, I have no idea if this will work as is, you might need to tweak how it passes the variable argument list 
    invokeSupersequent(format); 
} 

+(void)load { 
    //do your method swizzle in here, this is called one time only 
    Method origMethod = class_getClassMethod([NSString class], @selector(stringWithFormat:); 
    Method replMethod = class_getClassMethod([NSString class], @selector(clu_stringWithFormat:); 

    method_exchangeImplementations(origMethod, replMethod); 
} 

鉴于这可能是不可能的,否则就是一个非常糟糕的主意 - 你正在改变班级集群中特定班级的运作方式,苹果说“不要”不要这样做“。有充分的理由 - 如果有人调用[NSMutableString stringWithFormat:]会发生什么,或者分配一个NSString,然后调用-initWithFormat:?有很多有效的路径来创建一个格式的字符串,你会有一段悲惨的时间试图维护拦截所有这些的代码。我鼓励你重新思考这个设计 - 无论你想要解决什么问题,都可能有一种更容易和更可维护的方式来实现它。例如,添加您自己的字符串工厂。

+0

RyanR,谢谢你的信息。我知道类别,但它创建了一种新方法,重写我的代码以使用新类型是很可怕的,因为我在任何地方都使用NSString。所以我想只导入我的新MyNSString,它会替换现有的NSString方法。如果我想使用原始的,它更加灵活。在C++中,我可以使用#typedef将调用(如printf)重定向到我拥有的,但我不知道如何在objective-c中执行此操作。 – clu

+0

嗨RyanR,我已经更新了我的问题,但仍然陷入混乱的方法,你有什么建议吗?再次感谢。 – clu

+0

你可能很难重写你的代码来使用一个新的类,但是看看你现在正在经历多少麻烦,去做一些可能给你带来更大问题的事情。 – sosborn

相关问题