由于某些原因,我必须保留原始代码而不进行修改,但尝试将系统API重定向到我的代码中,然后回调原始代码。例如,我想在[NSString stringWithFormat:]
中做更多的事情。
现在我尝试使用方法调配。但似乎在main的运行时期间没有加载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:)];
}
答案 0 :(得分:2)
不确定你要做什么,这个问题有点不清楚。另外,不要尝试从NSString继承它,它是类集群的一部分,所以它只是覆盖一个方法要复杂得多。你甚至不得不mplement the storage yourself。快速复杂。
有两种方法可以在Objective-C中为现有类添加方法:Categories和method 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);
}
鉴于这可能是不可能的,否则是一个非常糟糕的想法 - 你正在改变类集群中特定类的运作方式,Apple称“不要这样做”。并且有充分理由 - 如果someones调用[NSMutableString stringWithFormat:],或者分配NSString然后调用-initWithFormat:会发生什么?创建带有格式的字符串有很多有效的路径,你有一个悲惨的时间试图维护代码拦截所有这些。我鼓励你重新思考这个问题的设计 - 无论你想解决什么问题,实现它的方法都可能更简单,更易于维护。例如,添加自己的String Factory。