美文网首页
提高Objective-C方法调用速度(IMP caching)

提高Objective-C方法调用速度(IMP caching)

作者: 传说中的汽水枪 | 来源:发表于2019-06-09 10:30 被阅读0次

引言

《Objective-C高级编程iOS与OS X多线程与内存管理》这本书描述了IMP caching的技巧:


image.png

查看NSAutoreleasePool如下:


image.png

addObject:既是类方法又是实例方法。

Demo

就写了如下的测试代码:

// 代码1
typedef void (*_p_IMP)(id, SEL, ...);
// 代码2
static _p_IMP s_imp;
static SEL s_sel;

@implementation RXTMImpCachingObject

+ (void)initialize { // 代码3
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        id cls = [RXTMImpCachingObject class];
        s_sel = @selector(fake_autorelease_method:);
        s_imp = (_p_IMP)[cls methodForSelector:s_sel];
    });
}

+ (void)fake_autorelease_method:(NSString *)value {
    NSLog(@"fake_autorelease_method:%@", value);
}

- (void)test {
    // 代码4
    (*s_imp)([self class], s_sel, @"abc111");
}
@end

代码1:系统的IMP是

    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    typedef void (*IMP)(void /* id, SEL, ... */ ); 
    #else
    typedef id (*IMP)(id, SEL, ...); 
    #endif

因此需要重新定义如下:

typedef void (*_p_IMP)(id, SEL, ...);

参考:http://www.cocoachina.com/ios/20150717/12623.html
代码2: 定义了静态变量,存储sel与imp
代码3:懒加载类的时候,初始化sel与imp
代码4:使用函数指针的方式调用,而不是消息转发。

疑惑

实例方法能IMP Caching吗?

实例方法IMP Caching 方法一?

// 代码1
typedef void (*_p_IMP)(id, SEL, ...);
// 代码2
static _p_IMP s_instance_imp;
static SEL s_instance_sel;
@implementation RXTMImpCachingInstanceObject

+ (void)initialize { // 代码3
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        id cls = [RXTMImpCachingInstanceObject class]; // 代码4
        s_instance_sel = @selector(instanceMethod:); // 代码5
        s_instance_imp = (_p_IMP)[cls methodForSelector:s_instance_sel]; // 代码6
    });
}
// 代码7
- (void)instanceMethod:(NSString *)value {
    NSLog(@"instanceMethod:%@", value);
}
- (void)test {
    // 代码8
    (*s_instance_imp)([self class], s_instance_sel, @"abc111");
}
@end

代码8 会出现崩溃,崩溃信息:

[RXTMImpCachingInstanceObject instanceMethod:]: unrecognized selector sent to class 0x10780c288

实例方法IMP Caching 方法二?

既然方法一出现了unrecognized selector 错误,那么可以更改如下:

// 代码1
typedef void (*_p_IMP)(id, SEL, ...);
// 代码2
static _p_IMP s_instance_imp;
static SEL s_instance_sel;
@implementation RXTMImpCachingInstance2Object

+ (void)initialize { // 代码3
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        s_instance_sel = @selector(instanceMethod:); // 代码4
    });
}
// 代码5
- (void)instanceMethod:(NSString *)value {
    NSLog(@"instanceMethod:%@", value);
}
- (void)test {
    // 代码6
    s_instance_imp = (_p_IMP)[self methodForSelector:s_instance_sel];
    // 代码7
    (*s_instance_imp)([self class], s_instance_sel, @"abc111");
}
@end

把s_instance_imp赋值的时候,是使用一个实例对象赋值
这样就可以了。

实例方法IMP Caching 方法三 最终极方案

实现方案二的时候,难道每个实例对象都需要维护这个IMP吗?想想就觉得有可能有问题,那在iOS的黑魔法 Method sizzle是如何实现的了。也不是每个实例对象都需要交换方法,突然想起,根据方法一的错误提示,我可以直接换成对象:

// 代码1
typedef void (*_p_IMP)(id, SEL, ...);
// 代码2
static _p_IMP s_instance_imp;
static SEL s_instance_sel;
@implementation RXTMImpCachingInstance3Object

+ (void)initialize { // 代码3
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        id cls = [RXTMImpCachingInstance3Object new]; // 代码4
        s_instance_sel = @selector(instanceMethod:); // 代码5
        s_instance_imp = (_p_IMP)[cls methodForSelector:s_instance_sel]; // 代码6
    });
}
// 代码7
- (void)instanceMethod:(NSString *)value {
    NSLog(@"instanceMethod:%@", value);
}
- (void)test {
    // 代码8
    (*s_instance_imp)([self class], s_instance_sel, @"abc111");
}
@end

把代码4从

id cls = [RXTMImpCachingInstanceObject class];

换成:

id cls = [RXTMImpCachingInstanceObject new];

这也是不是表明我对类对象和实例对象还是理解的不到位!

相关文章

网友评论

      本文标题:提高Objective-C方法调用速度(IMP caching)

      本文链接:https://www.haomeiwen.com/subject/jtrgxctx.html