安全方法 swizzling 的正确实现。
使用 method_exchangeImplementations
的经典方法 swizzling 相当简单,但它有很多局限性
+load
方法中执行时是安全的。如果您需要在应用程序的生命周期中 swizzle 方法则必须考虑到第三方代码可能在同一时间在另一个线程中对该方法进行 swizzling。_cmd
参数。 (通常您也无法确定 (5).)RSSwizzle 避免了所有这些已知的陷阱。
原始实现必须始终从新实现中调用。由于安全稳健的 swizzling 需要在调用时刻而不是 swizzling 时刻动态获取原始实现 (1,2),因此 swizzling API 有点不寻常。
下面是用于 swizzle -(int)calculate:(int)number;
方法的一个示例
RSSwizzleInstanceMethod(classToSwizzle,
@selector(calculate:),
RSSWReturnType(int),
RSSWArguments(int number),
RSSWReplacement(
{
// The following code will be used as the new implementation.
// Calling original implementation.
int res = RSSWCallOriginal(number);
// Returning modified return value.
return res + 1;
}), 0, NULL);
您也可以使用不带宏的 API,尽管它稍微复杂一些。
您应该传递一个工厂块,该块返回 swizzled 方法的新实现的块。并使用 swizzleInfo
参数来检索和调用原始实现。
下面是用于 swizzle -(int)calculate:(int)number;
方法的一个示例
SEL selector = @selector(calculate:);
[RSSwizzle
swizzleInstanceMethod:selector
inClass:classToSwizzle
newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
// This block will be used as the new implementation.
return ^int(__unsafe_unretained id self, int num){
// You MUST always cast implementation to the correct function pointer.
int (*originalIMP)(__unsafe_unretained id, SEL, int);
originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
// Calling original implementation.
int res = originalIMP(self,selector,num);
// Returning modified return value.
return res + 1;
};
}
mode:RSSwizzleModeAlways
key:NULL];
使用类似的 API 进行类方法 swizzling。
下面是用于 swizzle +(int)calculate:(int)number;
方法的一个示例
RSSwizzleClassMethod(classToSwizzle,
@selector(calculate:),
RSSWReturnType(int),
RSSWArguments(int number),
RSSWReplacement(
{
// Calling original implementation.
int res = RSSWCallOriginal(number);
// Returning modified return value.
return res + 1;
}));
混编通常伴随着检查这个特定的类(或其超类之一)是否已经被混编。在这里,mode
和 key
参数会有所帮助。可能的模式值
RSSwizzleModeAlways
RSSwizzle 在任何情况下都进行混编,不管给定的 key
是什么。RSSwizzleModeOncePerClass
如果相同的类之前已经用相同的 key
混编过,则 RSSwizzle 不会进行混编。RSSwizzleModeOncePerClassAndSuperclasses
如果相同的类或其超类之一之前已经用相同的 key
混编过,则 RSSwizzle 不会进行混编。以下是一个示例,仅当类以及其超类中均未用给定的 key
混编时,才混编 -(void)dealloc;
static const void *key = &key;
SEL selector = NSSelectorFromString(@"dealloc");
RSSwizzleInstanceMethod(classToSwizzle,
selector,
RSSWReturnType(void),
RSSWArguments(),
RSSWReplacement(
{
NSLog(@"Deallocating %@.",self);
RSSWCallOriginal();
}), RSSwizzleModeOncePerClassAndSuperclasses, key);
注意:
RSSwizzleModeOncePerClassAndSuperclasses
模式不能保证您的实现只在每次方法调用时被调用一次。如果混编的顺序是:首先继承类,然后超类;那么将会进行两次混编,新实现将会被调用两次。
RSSwizzle 完全线程安全。您不需要任何额外的同步。
Yan Rabovik (@rabovik on twitter)
MIT License.