检测项目中的循环引用引起的内存问题

说到检测项目中的循环引用 可以有很多手段,其中牛叉的 instruments 当然是把利器。

当然开发过程中往往会大意引起的 循环引用

比如:忘写了 @weakify(self) && @strongify(self); 在大量使用RAC 和 block.....

当然引起这类原因还有很多...

如果分工明确的话可能会再项目结束后,专门测试这块...然而好像并不是每次迭代都会做这块的工作,除非被明确发现引起崩溃的情况。

so  要是能把这个工作引入debug 期间,如果引起循环引用 可以抛出异常给开发人员,及时修复

这方面还是有些大牛写的工具的:如 FLEX

还有 HeapInspector-for-iOS  可以去看看

但有个缺陷时都需要开发人员自己进行分析(),否则还是无法暴露这类问题。

so  还是自己动手吧!

切入点在 UINavController 和 UIViewController  ,

app 最多的还是界面展示上,可能会对ViewController 频繁操作,如  push 一个VC、 present 一个VC

最理想的情况下(大部分情况下)你把vc 推出 nav栈 也就意味这系统要回收vc,当然要用到Nav 。

VC 中又会包含很多其它成员,最终直接获取间接的被  VC 所引用。如果VC 或 VC中的成员 遇到强引用循环,那么VC被推出NAV栈后

就不会被释放,如能抛出异常,进行分析的话就很好解决啦。

当然也会有例外的情况:

假如 VC1  有个@p(n,strong)VC2 的成员属性。然后再 VC1中 使用nav 推出 VC2.  VC2被推出nav栈并不会释放。

但是 如果VC1也可以被推出Nav栈的话 必定会被释放,如果不是rootVC的话(除非它又被 另一个VC强引用着,这也太奇葩啦。。)

正常情况很好解决,在vc 被推出nav 栈后开一个计时器 2s内如果vc 没有被释放,则抛出一个异常。

这里会有一个情况:

vc 确实被释放啦,当在2s 后才释放,也就时延时啦,(2s 内正常情况下vc 都会释放的)。引起这种原因很多,

vc 被一些"复杂的引用关系" 直接或间接的引用啦,造成vc 释放时等着什么东西释放。

比如使用

vc 中使用[self performSelector:@selector(xxx) withObject:nil afterDelay:10]; 我在进入vc 4s后就推出,

那么vc 会等着这个10s的任务完成后才会被释放。试想我vc 都被推出去拉(看不到啦),在去执行一个任务有多大意思

除非是一些必要的操作,如果是关于ui的操作就更不应该啦。

这种情况当然应该避免,程序中保持简单高效的引用是很有必要的。尤其内存方面。

代码很简单,对于那个例外(vc 被强引用啦),开发人员本意就是要让vc 被强引用,使用了上面逻辑的代码每次都会死在某些地方。

我还忽略不了,必须重启。又死在同一个地方...。估计会被骂死。。

so 。如果可以忽略掉那个异常,是不是就方便多拉。(断言,抛出异常、其它机制都会使程序挂掉)如果能用代码打个断点,岂不妙哉。

最后来看看那个例外。我怎么知道vc 在被推入nav 栈时被强引用啦??

最初相当 retainCount  .  大家都知道 这方法不靠谱,完全没有什么意义。

期间我去重写 NSObject 的 release retain  一些内存管理的方法,试图自己记录retainCount.

然而结果并不是想象中那样,一个完全没有被强引用的VC 初始化出来,然后被加入到nav 栈之前,我记录的retainCount 已经是5还是6来着。。

确实困扰了一两天,有时候长时间想不出的问题,可能已经在牛角尖啦0.0

如何变通,如果能确定vc 是某一个VC的成员是不是就可以解决啦。
so 变成 给定实例  A 和 B  如何确定实例B 是 实例A的成员。

可以根据A 去Class 里找ivar 列表。 然后对比每一个ivar 和 B 对比,如果一样那么就能确定啦,而且还可以获取实例B 的变量名

像这样:

#import <objc/runtime.h>

static id GetIvarName(id holdInstance,id ivarInstance){
    if (!holdInstance||!ivarInstance) {
        return nil;
    }

    NSString *ivarName = nil;
    uint32_t ivarCount;
    Ivar *ivars = class_copyIvarList([holdInstance class], &ivarCount);
    if(ivars)
    {
        for(uint32_t i=0; i<ivarCount; i++)
        {
            Ivar ivar = ivars[i];
            const char*ivarType = ivar_getTypeEncoding(ivar);
            NSString *ivarTypeStr = [NSString stringWithCString:ivarType encoding:NSUTF8StringEncoding];
            //成员变量不是object 调用object_getIvar 会crash
            if (![ivarTypeStr hasPrefix:@"@"]) {
                continue;
            }

            id pointer = object_getIvar(holdInstance, ivar);

            if(pointer == ivarInstance)
            {
                ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
                break;
            }
        }

        free(ivars);
    }
    return  ivarName;
}

上面例外情况基本解决:

在hook 的 push 是 可以确定

    if ([self.viewControllers count]>0) {
        UIViewController *lastVC = [self.viewControllers lastObject];
        NSString *ivarName = GetIvarName(lastVC, viewController);
        viewController.isStrongRef = ivarName?YES:NO;
        if (viewController.isStrongRef) {
            DDLogWarn(@"****[%@]**被[%@]的成员变量[%@]强引用**,在%@ pop后将不会被释放,如有必要,请忽略该条信息.",[viewController class],[lastVC class],ivarName,[viewController class]);
        }
    }

和 hook 的present中

    NSString *ivarName = GetIvarName(self, viewControllerToPresent);
    viewControllerToPresent.isStrongRef = ivarName?YES:NO;
    if (viewControllerToPresent.isStrongRef) {
        DDLogWarn(@"****[%@]**被[%@]的成员变量[%@]强引用**,在%@ dismiss后将不会被释放,如有必要,请忽略该条信息.",[viewControllerToPresent class],[self class],ivarName,[viewControllerToPresent class]);
    }

以上仅可用在debug 。。

不过遗憾的是,只能检测nav uiviewController 相关的循环引用。应该可以覆盖大部分情况啦。

代码在这里: git clone [email protected]:githhhh/ProbeRefCycles.git

好啦,小试牛刀后下篇 来试试 Instrument 这把利器 检测循环引用....

时间: 2024-08-05 12:25:16

检测项目中的循环引用引起的内存问题的相关文章

如何在 iOS 中解决循环引用的问题

稍有常识的人都知道在 iOS 开发时,我们经常会遇到循环引用的问题,比如两个强指针相互引用,但是这种简单的情况作为稍有经验的开发者都会轻松地查找出来. 但是遇到下面这样的情况,如果只看其实现代码,也很难仅仅凭借肉眼上的观察以及简单的推理就能分析出其中存在的循环引用问题,更何况真实情况往往比这复杂的多: testObject1.object = testObject2; testObject1.secondObject = testObject3; testObject2.object = tes

ASP.NET项目中,无法引用App_Code中的类

ASP.NET项目中,无法引用App_Code中的类 最近在做Walmart的项目,其中一个付款系统是ASP.NET编写的.遇到一个问题就是无法引用App_Code中的类. 而且在后台代码中编写代码时,也没有智能提示.生成解决方案时会报错:对应的名称空间或类型不存在或找不到. 解决办法就是选中相应类文件,在属性中设置"生成操作"(Build Action),从内容(Content)修改为编译(Compile).

shell中while循环引用ssh命令的坑

原理shell代码如下: #!/bin/sh cat ../androidsrc | while read line do         ip=$(echo $line | awk '{print $1}')         srcdir=$(echo $line | awk '{print $2}')         destdir=$(echo $line | awk '{print $3}')         user=$(echo $line | awk '{print $4}')  

OC中的循环引用

造成循环引用的原因: 比如在main函数中创建了两个类的对象A和B,现在引用计数都是1.现在让A和B互相引用(A有一个属性是B对象),属性说明是retain,B有一个属性是A的对象,属性说明是retain),现在两个对象的引用计数都增加1,都变成2. 现在执行[A release]; [B release]; 此时创建出来得main函数已经释放了自己对对象的持有权,但此时A和B的引用计数都还是1,因为他们互相引用了. 这时你发现A和B将无法释放,因为想要释放A必须先释放B,在B的的dealloc

彻底分析block中的循环引用

我做了一个被push到的controller,pop的时候,controller会释放,解析详见注释,欢迎批评指正! #import "AnimaViewController.h" @interface AnimaViewController () @property (nonatomic, strong) UIView *view1; @property (nonatomic, strong) UIView *view2; @property (nonatomic, copy) vo

vue-cli 项目中绝对路径引用的相关资源 npm run build 后 打开页面报404错误

在项目中.通过vue-cli 搭建的项目,npm run dev时,以绝对路径引用的相关资源在npm run build 后,页面打开报相关资源404错误: 解决方法如下: 打开build/utils.js,在图中相应位置加入红框内容,其中值可能会有不同,若不同,自己配置成相应的即可. 原文地址:https://www.cnblogs.com/xzma/p/9162584.html

简单聊一聊JS中的循环引用及问题

本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解. 1. 什么是循环引用 当对象 1 中的某个属性指向对象 2,对象 2 中的某个属性指向对象 1 就会出现循环引用,(当然不止这一种情况,不过原理是一样的)下面通过代码和内存示意图来说明一下. function circularReference() { let obj1 = { }; let obj

javascript中的循环引用对象处理

先说明一下什么是循环引用对象: var a={"name":"zzz"}; var b={"name":"vvv"}; a.child=b; b.parent=a; 这里的a和b都是一个循环引用对象. 循环引用对象本来没有什么问题,序列化的时候才会发生问题,比如调用JSON.stringify()对该类对象进行序列化,就会报错: Converting circular structure to JSON.    而序列化需求很

在.NET4.5项目中添加HttpClient引用的办法

一. 创建新项目 1. 打开  Microsoft Visual Studio 201 0,然后从 “ 文件 ” 菜单中选择 “ 新建项目 ” .在模板列表中,选择  Visual C#.在该区域下面,选择 “Windows  应用 程序应用 ” ,然后选择 “ 空白应用程序 ” .将该应用程序命名为  winHttpClientBase,然后单击 “ 确定 ” . 2. 添加 Microsoft HTTP Client Libraries 的引用如下图 1 ,图 2 ,图 3 ,图 4 . 图