自释放

何为“自释放”?可以简单的理解为对象在生命周期结束后自动清理回收所有与其相关的资源或链接,这个清理不仅仅包括对象内存的回收,还包括对象解耦以及附属事件的清理等,比如定时器的自我停止、KVO对象的监听移除等

对象内存的回收

开发中,对象管理的基本原则——谁创建谁释放。但是,非ARC工程中,我们会用autorelease来标记一个对象,告诉编辑器,这个对象我不负责释放,此时,这个对象就变成了“自释放”对象,当其不再需要时,系统就会自动回收其内存。而ARC工程中,所有对象对于我们来说都是自释放对象,很高兴,我们不再需要处处留意内存泄露的问题,可以把更多的精力放在业务逻辑上,但是这并不意味着真的没有内存泄露,试试这个工具HJNSObjectRelease,也许你会有意想不到的收获。

定时器的自释放

定时器与一般对象不同,当创建完定时器后,其并不会自我释放,需要在适当时刻invalidate。在实际开发中,也许你经常会这样创建定时器

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimerCount) userInfo:nil repeats:YES];

然后在dealloc函数中将定时器invalidate。很遗憾,你会发现程序永远也不会执行到dealloc函数,因为NSTimer强引用target对象,循环引用的出现必然导致内存泄露。此时,你肯定非常想要一个weak target的定时器,很高兴,MSWeakTimer很好的满足了你的需求。但是,Timer仍然没有自我释放,你仍然需要在dealloc中将其invalidate。那么,如何才能不写invalidate?定时器能否自释放?我们先把这个问题放在一边,接着往下看

KVO的自释放

iOS开发中,经常会用到消息通知及KVO,也许你会这样写代码

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onNotice) name:@"NoticeIdentifier" object:nil];
    [self addObserver:target forKeyPath:@"keyPath" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NoticeIdentifier" object:nil];
    [self removeObserver:target forKeyPath:@"keyPath"];
}

随着时间的积累,你会非常习惯这种写法,并且苹果也是这样推荐的。但是慢慢你会发现所有对象的Dealloc函数都只做了这一件事,能不能不做这件事?FBKVOController也许会是一个不错的选择,Demo可以这样写

[self.KVOController observe:clock keyPath:@"date" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew block:^(ClockView *clockView, Clock *clock, NSDictionary *change) {
    clockView.date = change[NSKeyValueChangeNewKey];
}];

FBKVOController的实现原理可以查看这篇文章,通过自释放的实现,程序猿不再关心remove监听。但是其还是有一定的局限性——对象无法监听自己的属性,如果你的代码是这样的

    [self.KVOController observe:self keyPath:@"date" options:NSKeyValueObservingOptionNew block:^(NSDictionary *change) {
    // to do
}];

很遗憾,循环引用的问题又出现,因为FBKVOController中的NSMapTable对象会retain key对象,具体代码如下

[_objectInfosMap setObject:infos forKey:object];

那么,FBKVOController是如何做到自释放的?可以归纳为四个字——动态属性。其为观察者绑定动态属性self.KVOController,动态绑定的KVOController会随着观察者的释放而释放,KVOController在自己的dealloc函数中移除KVO监听,巧妙的将观察者的remove转移到其动态属性的dealloc函数中。

可是,这又有什么用?对象仍然无法监听自己的属性,还是要重写set函数。HTBKVObservation也许会改变你的想法,其和FBKVOController来自同一人,代码可以这样写

    self.anObservation = [HTBKVObservation observe:anObjectToObserve keyPath:@"observeMe" options:0 callback:^(HTBKVObservation *observation, NSDictionary *changeDictionary) {
   // to do
}];

HTBKVObservation并没用采用动态属性,而是采用属性的方式实现自释放。可以监控对象自己的属性,但是需要创建属性HTBKVObservation。 这里我对其做了一点扩展,方便使用,代码可以这样写

    [self observe:self keyPath:@"KVOPath" options:NSKeyValueObservingOptionNew callback:^(HTBKVObservation *observation, NSDictionary *changeDictionary) {
   // to do
}];

FBKVOController 和HTBKVObservation通过属性或动态属性巧妙的将KVO的remove转移给第三者,实现了KVO事件的解耦,为自释放的实现提供了一种借鉴思路

NSNotification的自释放

谈完 KVO,再来谈谈NSNotification。针对Notification,ReactiveCocoa做了很好的封装,网上有很多介绍其如何使用的文章,在此不再累述。直接看代码

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidChangeFrameNotification object:nil] subscribeNext:^(id x) {
        // to do
    }
];

简单明了,当观察者dealloc,很遗憾,NSNotification并没用移除,因为对象并没用自释放,正确代码应该是这样

[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidChangeFrameNotification object:nil] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id x) {
        // to do
    }
];

ReactiveCocoa自释放的原理与FBKVOController不同,其并不是通过属性或者动态属性的方式实现,而是通过swizzling观察对象的dealloc函数,在自定义dealloc函数实施清理,但不是默认清理,需要我们告诉它willDeallocSignal的时候完成所有清理工作。

除了定时器、KVO、NSNotification,包括封装的某个功能对象,比如HttpRequest,或者数据库ListSql等,合理的利用自释放可以给使用者带来更多的便利,同时也会减少 crash 产生的概率。实现自释放的方法可以总结为以下三种方式

  1. 动态属性的自释放
  2. @property 的自释放
  3. swizzling dealloc的自释放
时间: 2024-10-27 13:07:44

自释放的相关文章

函数一直无法立即退出,在等待了大约30s后才能退出(QMulitHash释放不连续的内存需要很长世间,而这样设置局部变量后又无法避免这个问题)

局部变量使用对性能的影响以及进程的堆和栈: 由于在代码中我使用了QMulitHash<QString , LHFilteVersionItem> tmp;这一局部变量来保存某一目录下的文件,由于在写测试代码期间,我利用循环模拟了50万的数据序列化后保存在文件中,在运行期间我发现读取函数耗费很长的时间,而函数里面最耗时的读取操作也只花费了很短的时间,但是函数一直无法立即退出,在等待了大约30s后才能退出,相关代码如下: [cpp] view plain copy void LHTWORKFLOW

幸运的被释放

我们,也许出生不同,也许经历不同,但是我想,成长的过程,大部分人还是有相同的心路可寻的.比如说,步入新环境的青涩和幼稚.迷失了自我的慌乱和惶惑.甚至想要放弃自己的"出溜".压力下的软弱或坚强等等. 我们常常忽略少年愁,其实,少年的愁闷一点也不比中年的愁闷肤浅,而是两种不同的愁闷.我们在心智还不成熟的时候认识世界,成长与错误同行,经常活在对过去的悔恨或者是对未来的幻想中,于是在愁苦和矛盾中成长.而中年人的愁闷,大概是对无法改变现状乏力的愁.但是,我们常常觉得自己比较愁,这简直像是在比惨,

win10怎么关闭小娜助手释放电脑内存

不知道大家有没有觉得微软小娜助手在手机上挺实用,但是在电脑上任务栏上占用地方较大,真实的使用频率却很低,不仅仅如此而且小娜还会占用较多的电脑内存.今天我们就来说说怎么关闭小娜助手释放电脑内存. 一.同时按键盘上的Win+S键,打开小娜界面,点击左侧的齿轮图标. 二.把"Cortana可以提供建议.想法.提醒.通知等"这一项关闭,再将下面的两项也关掉. 三.点击[管理Cortana在云中了解到的我的相关内容],跳转至现金网官网并登陆账号,找到"其他Cortana数据以及个性化语

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷

PHP如何释放内存之unset销毁变量并释放内存详解

PHP的unset()函数用来清除.销毁变量,不用的变量,我们可以用unset()将它销毁.但是某些时候,用unset()却无法达到销毁变量占用的内存!我们先看一个例子: <?php $s = str_repeat('1',255); //产生由255个1组成的字符串 $m = memory_get_usage(); //获取当前占用内存 unset($s); $mm = memory_get_usage(); //unset()后再查看当前占用内存 echo $m-$mm; ?> 最后输出u

易宝典文章——玩转Office 365中的Exchange Online服务 之二十七 怎样处理并释放误报隔离邮件

在Exchange Online中有众多的垃圾邮件过滤功能,其过滤的结果大致分为四类: >直接拒绝接收: >放入垃圾邮件文件夹: >主题中进行标记为垃圾邮件: >隔离对于直接拒绝接收这种情况大多会针对确切到发件人.发件域.发件服务器的IP地址来进行设置,所以很少会出现误拒的情况.而放入垃圾邮件文件夹和主题中进行标注,这些垃圾邮件实际已经到了用户的邮箱.即使出现误报,用户也可以看到邮件.只有最后一种隔离邮件,如果是将用户所需要的邮件进行了隔离,那么用户往往会申请要找回这封邮件.怎样才

android里单例对象和某些数据被释放的问题

接触正式的android开发已经有一段时间了,项目的第一个版本终于快完成了.有一次自己在测试的时候,把自己的android项目切到后台,同时打开了几个应用之后重新切回到自己的app,发现报错了.经过排查,发现是自己的单例对象中的数据被释放掉了,也就是int变量的值 变成了0,string变量的值变成了null. 我的单例一开始是这样的(举例); public class UserInfo { private static UserInfo userInfo = null; private int

关于Activity调用Ondestroy()方法之后内存管理器为什么没有释放占用资源

最近在研究activity 执行了finish之后为什么还有很多资源没有释放的问题,关于这个原因的产生,最直接的想法就是activity里面还有很多资源没有手动释放,因为大家知道,activity只不过是一个高度抽象的UI组件,他仅仅只是一个控制界面的功能,包括分发touch时间还有一些列的作用,展示界面的工作是交给DecorView下的所有view以及viewGroup,所以我们可以认为activity持有了所有在他内部绑定的view的引用,但是这些view不仅仅只有activity的引用,还

windows server 2008 大量拷贝后释放内存

管理的服务器中有一台windows 2008,这台服务器最近每天都会拷贝几十万的图片,拷贝量非常大,维护时发现每次拷贝完,操作系统的内存使用都会接近100%,导致没有办法进行其它操作,前几次都通过下班后重启服务器解决,今天仔细查了一下,找到了问题的原因 问题描述: server 2008 r2系统会在进行大量IO操作时,占据大量内存资源,直至内存占满,从而导致系统运行速度变慢. 相关现象:1. 内存占用率90%以上(即是是64G内存,也会占用63.5G)2. 资源管理器中所有进程内存和较低(约1

Linux中删除文件,磁盘空间未释放问题追踪

在客户使用我们产品后,发现一个问题:在删除了文件后,磁盘空间却没有释放.是有进程在打开这个文件,还是其他情况?我们一起来看看一下两个场景 一. 场景一:进程打开此文件 当一个文件正在被一个进程使用时,用户删除此文件,文件只会从目录结构中删除,但并没有从磁盘删除.当使用这个文件的进程结束后,文件才会真正的从磁盘删除,释放占有的空间. 我们发现剩余磁盘空间比较少时,回去删除一些大的临时文件或者log文件,如果删除之后会发现磁盘空间并未减少,那么可以通过"lsof"命令去查看正在使用该文件的