Objective-C Method Swizzling 示例不起作用

Posted

技术标签:

【中文标题】Objective-C Method Swizzling 示例不起作用【英文标题】:Objective-C Method Swizzling example not working 【发布时间】:2014-11-21 13:24:50 【问题描述】:

我想尝试 Method Swizzling 以清楚地了解它是如何工作的。 查看此代码:http://nshipster.com/method-swizzling/。 我已经创建了一个类和一个类别,这里是代码

#import <Foundation/Foundation.h>

@interface CustomClass : NSObject

-(void) originalMethod;

@end

类的实现

#import "CustomClass.h"

@implementation CustomClass

-(id) init 
    self = [super init];
    return self;


-(void) originalMethod 
    NSLog(@"I'm the original method");


@end

类别标题:

#import "CustomClass.h"

@interface CustomClass (CustomCategory)

-(void) newMethod;

@end

分类实现

#import "CustomClass+CustomCategory.h"
#include <objc/runtime.h>

@implementation CustomClass (CustomCategory)

-(void) newMethod 
    [self newMethod];
    NSLog(@"I'm the new method");


+(void) load 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        Class class = [self class];

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);

        SEL originalSelector = @selector(originalMethod:);
        SEL swizzledSelector = @selector(newMethod:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class,
                                            originalSelector,
                                            method_getImplementation(swizzledMethod),
                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) 
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
         else 
            method_exchangeImplementations(originalMethod, swizzledMethod);
        
    );


@end

这里是主要的:

#import <Foundation/Foundation.h>
#import "CustomClass.h"
#import "CustomClass+CustomCategory.h"

int main(int argc, const char * argv[]) 
    @autoreleasepool 
        CustomClass *cc = [[CustomClass alloc] init];
        [cc newMethod];
    
    return 0;

当我调用[cc newMethod] 时,我得到了无限循环,根据我链接的文章,这不应该发生。 我在这段代码中看不到错误。

【问题讨论】:

你确定调用的是load方法吗? @user1963877 不,我没有检查过。应该调用它,因为它是 NSObject 中存在的方法。 这里有一些关于使用+(void)loadlink的内容。请注意,您的教程涉及普通的 CocoaTouch 应用程序(带有 ui 线程等),而您在基本上是裸露的 hello world 程序上尝试它,所以它的行为可能不同。 @user1963877 不会有所作为。 我知道你发现了问题,但还有一件事:这本身并没有错,但你是从main() 调用[cc newMethod]。当您混合方法时,通常的意图是新方法是隐藏的实现细节。客户将使用旧方法,而 swizzling 的目的是改变其行为。我知道您可能只是在尝试,但想确保您了解它应该如何工作。 【参考方案1】:

问题出在语法上,而不是

SEL originalSelector = @selector(originalMethod:);
SEL swizzledSelector = @selector(newMethod:);

我应该写:

SEL originalSelector = @selector(originalMethod);
SEL swizzledSelector = @selector(newMethod);

xCode 只给了我一个警告,所以我认为选择器名称没有错。

【讨论】:

【参考方案2】:
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));

method_getImplementation(originalMethod) 不应该是swizzledMethod吗?

【讨论】:

【参考方案3】:

你好像在混用类方法:

Class class = [self class];

指向+load 上下文中的元类,尝试显式指定它,即

Class class = [CustomClass class];
// or
Class class = self;

另外,@bbum 说了什么

【讨论】:

【参考方案4】:

    语法错误,运行时会发送不同的消息。

    SEL originalSelector = @selector(originalMethod);
    SEL swizzledSelector = @selector(newMethod);
    

    如果第一种方法没有效果。试试看:

    BOOL didAddMethod =
    class_addMethod(class, originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) 
        method_exchangeImplementations(originalMethod, swizzledMethod);
    
    
    

交换实施可能产生的影响。

【讨论】:

以上是关于Objective-C Method Swizzling 示例不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C Method Swizzling 示例不起作用

Objective-C Runtime 运行时之四:Method Swizzling

iOS面试粮食Runtime—消息传递和转发机制Method Swizzling

iOS面试粮食Runtime—消息传递和转发机制Method Swizzling

不能 JSExport 具有多个参数的 Objective-C 方法吗?

深入理解Objective-C:方法缓存