引言
《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];
这也是不是表明我对类对象和实例对象还是理解的不到位!
网友评论