IOS 使用runtime swizzling(黑魔法的那些事)

来源:互联网 发布:网络歌手 金达莱花 编辑:程序博客网 时间:2024/05/17 23:38

1.为了防止数组越界崩溃和字典添加空对象崩溃,扩展了以下两个类导致的问题。

突然发现如果我在键盘出现的状态下,按下Home键使得App从Foreground切换到Background的时候,App就会触发一个exception导致crash,这个crash的信息在我看来很奇怪。

[UIKeyboardLayoutStar release]: message sent to deallocated instance

最开始,我想,干脆去掉这个库,不用了。去掉后,发现运行App就出错,出错的原因是某处代码会把nil给设置到NSMutableDictionary中,而NSMutableDictionary不能储存nil。结果,我还不能直接把该库给从工程中移除。

最后想想,怎么办呢,看来只能改一改代码了。
首先第一步,需要确定该改哪里,接下来就要确定该怎么改。
经过一阵子的comment/uncomment操作,最终发现,如果不swizzle NSArray的objectAtIndex,App就不会出现UIKeyboardLayoutStar的exception。
该怎么改呢?有一个经验,就是,涉及比较多runtime的代码,最好不要用arc,所以我决定把我司用到的几个文件改为MRC。
仔细看了那几个文件,发现还是比较好改的。只需要做如下改动就行了
  1. 初始化所有的NSError变量声明,原代码都是NSError *err;的形式,在ARC下没问题的,compiler都给自动初始化为nil了,但是MRC下不行
  2. 把NSError从函数返回的时候,加上autorelease

照上面两点改好后,再到build phase里给那几个m文件加上-fno-objc-arc flag

接下来又测试了几次,UIKeyboardLayoutStar release这个exception没有再出现,问题解决。

顺便再多说几句,DurexKit的作者也提到了某几个文件不要用ARC。

CUSLayout
2014-04-09 17:48:58
回复
@habib狂鳄– : 这两个发现一个缺陷,已经修正了,请试一下最新版,最新版如果用源码需要把NSObject+SafeKit标记为-fno-objc-arc,或者直接用静态库
不过作者所指的那个文件不能解决我这个问题,因为本文的问题是由于swizzle了NSArray的方法引起的,而不是NSObject。

#import "NSArray+Swizzling.h"

#import <objc/runtime.h>


@implementation NSArray (Swizzling)


+ (void)load

{

    [superload];

    //1.目标方法

    Method fromMethod =class_getInstanceMethod(objc_getClass("__NSArrayI"),@selector(objectAtIndex:));

    //2.修改方法

    Method toMethod =class_getInstanceMethod(objc_getClass("__NSArrayI"),@selector(lzx_objectAtIndex:));

    //3.交换方法

    method_exchangeImplementations(fromMethod, toMethod);

    

    Method fromMMethod =class_getInstanceMethod(objc_getClass("__NSArrayM"),@selector(objectAtIndex:));

    //2.修改方法

    Method toMMethod =class_getInstanceMethod(objc_getClass("__NSArrayM"),@selector(lzxm_objectAtIndex:));

    //3.交换方法

    method_exchangeImplementations(fromMMethod, toMMethod);

    

}



/**

 用来输出数组越界崩溃信息


 @param index 索引

 @return 返回数据

 */

- (id)lzx_objectAtIndex:(NSInteger)index

{

    if (index >=self.count)

    {

        //异常处理

        NSLog(@"-----------------------crash -----------------------\n");

        NSLog(@"reason : [%@ %@] index {%lu} beyond bounds [0...%lu]",

              NSStringFromClass([self class]),

              NSStringFromSelector(_cmd),

              (unsignedlong)index,MAX((unsignedlong)self.count -1, 0));

        returnnil;

    }

    else

    {

        return [selflzx_objectAtIndex:index];

    }

}


- (id)lzxm_objectAtIndex:(NSInteger)index

{

    if (index >=self.count)

    {

        //异常处理

        NSLog(@"[%@ %@] index {%lu} beyond bounds [0...%lu]",

              NSStringFromClass([self class]),

              NSStringFromSelector(_cmd),

              (unsignedlong)index,MAX((unsignedlong)self.count -1, 0));

        returnnil;

    }

    else

    {

        return [selflzxm_objectAtIndex:index];

    }

}


@end



#import "NSMutableDictionary+Swizzling.h"

#import <objc/runtime.h>


@implementation NSMutableDictionary (Swizzling)


+ (void)load

{

    [superload];

    //1.目标方法

    Method fromMethod =class_getInstanceMethod(objc_getClass("__NSDictionaryM"),@selector(setObject:forKey:));

    //2.修改方法

    Method toMethod =class_getInstanceMethod(objc_getClass("__NSDictionaryM"),@selector(swl_setObject:forKey:));

    //3.交换方法

    method_exchangeImplementations(fromMethod, toMethod);

}


/**

 用来输出字典设置空对象错误信息


 @param swlObject 目标对象

 @param key 关键字

 */

- (void)swl_setObject:(id)swlObject forKey:(NSString *)key

{

    if (swlObject ==nil)

    {

        @try {

            [selfswl_setObject:swlObject forKey:key];

        } @catch (NSException *exception) {

            //错误处理

            NSLog(@"----------------------crash----------------------\n");

            NSLog(@"reson : --- %s crash Method %s---------\n", class_getName(self.class),__func__);

            NSLog(@"%@",[exception callStackSymbols]);

            swlObject = [NSStringstringWithFormat:@""];

            [selfswl_setObject:swlObject forKey:key];

            

        } @finally {

            

        }

    }

    else

    {

        [selfswl_setObject:swlObject forKey:key];

    }

}


@end




原创粉丝点击