iOS野指针

作者: ch32053 | 来源:发表于2020-04-06 16:24 被阅读0次

    最近在调研野指针的定位工具,对野指针有了更深入的理解,写篇文章总结下。

    一、那什么是野指针?这是维基百科上的定义:

    当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称迷途指针(即通常说的野指针)。

    可以看出普通指针变成野指针需要满足两个条件:
    1、其所指向的对象被释放或者收回,2、其本身没有做任何的修改。

       // ARC环境下:
        __unsafe_unretained UIView *testObj1 = [[UIView alloc] init];
       // testObj1 指向的内存区域已释放,本身未做任何修改,已成为野指针,向其发消息会闪退。
    
        __weak UIView *testObj2 = [[UIView alloc] init];
       // testObj2 指向的内存区域已释放,但本身被置为nil,不会成为野指针。
    

    需要指出一点:访问野指针本身是没有问题的,不会引起异常;只有使用野指针时才会异常(比如OC里给对象发消息),表现就是闪退。

      // ARC环境下
        __unsafe_unretained UIView *testObj = [[UIView alloc] init];
        NSLog(@"testObj 指针指向的地址:%p 指针本身的地址:%p", testObj, &testObj);
        [testObj setNeedsLayout];
        // 可以看到NSlog打印不会闪退,调用[testObj setNeedsLayout];会闪退
    

    二、什么情况下会产生野指针?

    从产生过程上分析,有以下几种情况:
    (1)指针变量未初始化:任何指针变量刚被创建时不会自动成为NULL指针,所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
    (2)指针释放之后未置空:有时指针在dealloc或free后未赋值 NULL\nil,dealloc或free后它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。
    释放后的指针应立即将指针置为NULL\nil,防止产生“野指针”。
    (3)指针操作超越变量作用域:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

    第一点主要指使用C语言时。但是给初始化的指针赋个默认值是个好习惯,也是有必要的,不同的平台和编译环境对未初始化的指针的处理是有差异的。比如ARC环境下,定义NSInteger num,num 就可能被初始化成一个极大值。

    下图是使用野指针可能出现的情况:


    image.png

    三、怎么检测野指针?

    Xcode已经提供了比较完善的检测方法,Malloc ScribbleZoombie Objects。也可以自己实现这两种检测方法。
    1、实现Xcode的Malloc Scribble功能,Hook系统的free()函数,可以适用于c\c++类,标记每个要释放的对象:修改其指针指向的内存区域,在指针指向的内存区域填充数据(0x55), 使指针指向不可读的内存,再给这个对方发消息时,就会闪退。

    static void (*orig_free)(void *);
    void safe_free(void* p){
        size_t memSiziee= malloc_size(p);
       // 给指针指向的内存区域填充值,指针本身没有发生改变
        memset(p, 0x55, memSiziee);
        orig_free(p);
        return;
    }
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
           // 使用fishhook库,替换系统的free(void *)函数实现
            rebind_symbols((struct rebinding[1]){{"free", safe_free, (void *)&orig_free}}, 1);
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    

    2、实现Xcode的Zoombie Objects功能,实现自己的Zoombie Object(僵尸类),然后Hook系统的- (void)dealloc方法,只适用于oc类,修改对象的 isa 指针,令其指向特殊的僵尸类。僵尸类只有isa指针,没有任何方法实现,如果这个对象再次收到消息,就会闪退。

    - (void)dealloc
    {
       const char *className = object_getClassName(self);
       char *zombieClassName = NULL;
       do {
           Class zombieClass = objc_getClass(zombieClassName);
    
           objc_destructInstance(self); //关键
    
           object_setClass(self, zombieClass);
    
       } while (0);
    
       if (zombieClassName != NULL)
       {
           free(zombieClassName);
       }
    }
    

    这两种方法都是在产生野指针后,使用野指针时,让APP崩溃来发现是那里出的问题。也就是说需要尽量覆盖各种场景,才能发现野指针,并不能用扫描的方式提早发现。所以还是需要保证编码质量,从源头上避免出现野指针的问题。

    参考资料:
    https://blog.csdn.net/weibo1230123/article/details/81476085
    https://www.jianshu.com/p/4c8a68bd066c
    https://www.jianshu.com/p/8aba0ee41cd7

    相关文章

      网友评论

        本文标题:iOS野指针

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