iOS黑魔法--NSArray的拦截

接上一篇的iOS黑魔法来看下NSArray的拦截方法。拦截的主要就是一些常用的方法,比如index访问越界,add remove操作等,笔者列出自己的封装,不过你也可以根据自己的需要添加更多的拦截方法。
首先要明白NSArray是用了类簇的方法隐藏了内部的实现细节,NSArray也只是一个抽象的基类,用NSArray创建的对象并不是NSArray对象,而是它所封装的实体子类。
OC中并不能指明哪个类是抽象基类,需要我们自己去阅读文档来判断,
弄懂了这一点你就可以正确的进行方法的拦截了。说到这里就直接上代码了:

代码块

@implementation NSArray (xzSwizzle)

+ (void)load {
    [objc_getClass("__NSArray0") xzSwizzleMethod:@selector(objectAtIndex:) withMethod:@selector(xz_objectAtIndex:) error:nil];
    [objc_getClass("__NSSingleObjectArrayI") xzSwizzleMethod:@selector(objectAtIndex:) withMethod:@selector(xz_objectAtIndex:) error:nil];
    [objc_getClass("__NSArrayI") xzSwizzleMethod:@selector(objectAtIndex:) withMethod:@selector(xz_objectAtIndex:) error:nil];
}

- (id)xz_objectAtIndex:(NSUInteger)index {
    if (index >= self.count) {
        NSAssert(NO, @"---index越界!!!");
        return nil;
    }
    return [self xz_objectAtIndex:index];
}

@end


@implementation NSMutableArray (xzSwizzle)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [objc_getClass("__NSArrayM") xzSwizzleMethod:@selector(insertObject:atIndex:) withMethod:@selector(xz_insertObject:atIndex:) error:nil];
        [objc_getClass("__NSArrayM") xzSwizzleMethod:@selector(removeObjectAtIndex:) withMethod:@selector(xz_removeObjectAtIndex:) error:nil];
        [objc_getClass("__NSArrayM") xzSwizzleMethod:@selector(replaceObjectAtIndex:withObject:) withMethod:@selector(xz_replaceObjectAtIndex:withObject:) error:nil];
    });
}

- (void)xz_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (!anObject || index > self.count) {
        NSAssert(NO, @"---index越界或obj为nil!!!");
        return;
    }
    [self xz_insertObject:anObject atIndex:index];
}

- (void)xz_removeObjectAtIndex:(NSUInteger)index {
    if (index >= self.count) {
        NSAssert(NO, @"---index越界!!!");
        return;
    }
    [self xz_removeObjectAtIndex:index];
}

- (void)xz_replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
    if (!anObject || index >= self.count) {
        NSAssert(NO, @"---index越界或obj为nil!!!");
        return;
    }
    [self xz_replaceObjectAtIndex:index withObject:anObject];
}

@end

拦截的方法中添加了NSAssert,为了在debug环境中能够及时的发现这个问题,让其crash,不然发到线上岂不是很尴尬!当然上面的只是一部分拦截,你可以根据需要添加。
+load方法中的xzSwizzleMethod:withMethod:就是上篇文章提到的对method swizzle的封装,没有看到的朋友可以看下上篇文章。
另外发现有的博客中在+load中添加了

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{}

我想说,完全没必要,因为+load只在APP启动的时候执行一次,没必要添加单例,类对象本身就是一个单例!!!更多对于+load的知识可以百度一下,已经有很多大牛分析过。

问题

在添加这个拦截之后也不是一帆风顺的,因为线上莫名其妙的出现了几个崩溃:

[UIKeyboardLayoutStar release]: message sent to deallocated

发现这个问题之后我立马去排查,因为之前没有这个crash,在添加这个拦截之后就出现了,怀疑是这拦截机制有关系。复现方法在设备连接xcode的时候呼出键盘然后按home键,回到后台就会崩溃。而去掉objectAtIndex拦截之后就会正常,果然不出所料,猜测objectAtIndex应该不仅仅是取index下的对象还做了其他的操作,最终解决方案:添加子啊MRC下的同样的拦截就可以了,

@implementation NSMutableArray (xzSWizzleMRC)

+ (void)load {
  [objc_getClass("__NSArrayM") xzSwizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeMRC_objectAtIndex:) error:nil];
}

- (id)safeMRC_objectAtIndex:(NSUInteger)index {
    @autoreleasepool {
        if (index >= self.count) {
            NSAssert(NO, @"---index越界!!!");
            return nil;
        }
        return [self safeMRC_objectAtIndex:index];
    }
}

@end

因为笔者整个工程是在ARC环境下,所以不要忘记对文件进行标记-fno-objc-arc

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值