RSSwizzle 0.1.0

RSSwizzle 0.1.0

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布上次发布2014年12月

Yan Rabovik 维护。



RSSwizzle 0.1.0

  • Yan Rabovik

安全方法 swizzling 的正确实现。

动机

使用 method_exchangeImplementations 的经典方法 swizzling 相当简单,但它有很多局限性

  • 仅在 swizzling 在 +load 方法中执行时是安全的。如果您需要在应用程序的生命周期中 swizzle 方法则必须考虑到第三方代码可能在同一时间在另一个线程中对该方法进行 swizzling。
  • swizzled 方法必须由类本身实现,而不是由超类实现。从超类复制实现的解决方案不实际。必须在调用时刻获取原始超级类的实现,而不是在 swizzling 时刻。
  • swizzled 方法的实现不能依赖于 _cmd 参数。 (通常您也无法确定 (5).)
  • 可能有命名冲突 (3)

更多细节请参阅以下讨论:12345

RSSwizzle 避免了所有这些已知的陷阱。

用法

原始实现必须始终从新实现中调用。由于安全稳健的 swizzling 需要在调用时刻而不是 swizzling 时刻动态获取原始实现 (12),因此 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

您也可以使用不带宏的 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];

类方法 swizzling

使用类似的 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;
}));

模式

混编通常伴随着检查这个特定的类(或其超类之一)是否已经被混编。在这里,modekey 参数会有所帮助。可能的模式值

  • 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 完全线程安全。您不需要任何额外的同步。

要求

  • iOS 5.0+
  • Mac OS X 10.7+
  • ARC

作者

Yan Rabovik (@rabovik on twitter)

许可

MIT License.