Foundation中的类簇和Swizzle

参考链接:blog.sunnyxx.com/2014/12/18/…

我们都知道swizzle method在OC中是一种有趣又实用的技巧,现在假设我们想hook掉NSMutableArray的insertObject:atIndex:方法,我们会在NSMutableArray的分类中这样写:

swizzleInstanceMethod([self class], @selector(insertObject:atIndex:), @selector(swizzle_insertObject:atIndex:));
复制代码

然而我们会发现虽然swizzle成功了,但是实际上并没有执行swizzle_insertObject:atIndex:方法,这是为什么呢?

我们先写个测试代码:

NSString *nilStr = nil;
[[NSMutableArray array] addObject:nilStr];
复制代码

看下下面的错误信息:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
复制代码

__NSArrayM 是个什么鬼?这就是导致我们上面swizzle method后,swizzle_insertObject:atIndex:不执行的原因。

再来看以下的打印信息:

(lldb) po [[NSMutableArray alloc] init]
<__NSArrayM 0x7fb452e04a20>(

)

(lldb) po [NSMutableArray array]
<__NSArrayM 0x7fb452d0ba30>(

)

(lldb) po [NSMutableArray arrayWithObject:@""];
<__NSArrayM 0x7fb452d0baf0>(

)

(lldb) po [[NSMutableArray alloc] class]
__NSPlaceholderArray

(lldb) po [NSArray array]
<__NSArray0 0x7fb452d05910>(

)

(lldb) po [[NSArray alloc] init]
<__NSArray0 0x7fb452d05910>(

)

(lldb) po @[]
<__NSArray0 0x7fb452d05910>(

)

(lldb) po [[NSArray alloc] class]
__NSPlaceholderArray

(lldb) po [[NSArray alloc] initWithObjects:@"", nil];
<__NSArrayI 0x7fb452f059c0>(

)

(lldb) 
复制代码

仔细看其中的端倪,

[NSMutableArray array]  // __NSArrayM 类型(mutable)
[NSArray array]         // __NSArray0 类型 (空 immutable)
[[NSArray alloc] initWithObjects:@"", nil] // __NSArrayI 类型(immutable)
[[NSArray alloc] class] // __NSPlaceholderArray
[[NSMutableArray alloc] class]  // __NSPlaceholderArray
复制代码

可以看出,这些打印的类型是苹果隐藏起来的Array的实际类型,因此我们在文章开头的hook掉的是 [NSMutableArray class] 中的方法,并不是我们真正在项目中调用的 __NSArrayM 的 方法,所以并没有执行我们的swizzle_method。(有个小细节,3种方式打印的空不可变数组__NSArray0指向同一个地址

那该怎么解决这个问题?方法也很简单,现在知道了源方法属于哪个类型,那么我们就对这个类型实用swizzleMethod:

NSMutableArray *mutArray = [NSMutableArray array];
swizzleInstanceMethod([mutArray class], @selector(insertObject:atIndex:), @selector(swizzle_insertObject:atIndex:));
复制代码

上面还有一个奇怪的类型 __NSPlaceholderArray,这个又是干嘛的,在上面的参考链接中有它的解释,我们这里讨论它和swizzle method有什么关联,其实如果我们想hook掉NSArray的 init 方法的话就需要用到它了,代码如下:

NSArray *placeholderArray = [NSArray alloc];
swizzleInstanceMethod([placeholderArray class], @selector(initWithObjects:count:), @selector(swizzle_initWithObjects:count:));
复制代码

不止 NSArray,在Foundation中这样的类簇还有 NSDictionaryNSStringNSNumber,所以如果想hook这些类型的方法,就要注意源方法实际属于哪个子类型上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值