今天看到了OC的内存管理这块,觉得很亲切。

自己的习惯是尽量自己掌控程序的空间和时间,有点强迫症的感觉。用C和C++做项目的时候,时时刻刻都在操心这new和delete的配对使用和计数,学习stl和boost的时候看到了智能指针等时候,依然不是很爱使用,还是愿意坚持自己控制new和delete;后来用C#后,一直特别注意Dispose相关的动作,尽早释放对象占有的内存空间,避免无谓的占用一直到程序退出才释放。

OC中系统对每个实例对象地址都记录一个引用次数(当然有特例,见另外一篇随笔),这就是引用计数,在很多面向对象语言中都被采用。

这里总结一下OC中引用计数的显式操作过程:

1. 计数置1:每个实例对象在实际显式alloc创建的时候都会做到引用计数置1。

2. 计数加1: 向对象显式发送retain消息会将引用计数加1。

3. 计数减1: 向对象显式发送release消息会将引用计数减1。

4. 计数为0: 当对象引用计数为0时候,OC系统会自动向对象发送实际释放的消息dealloc。

5. 计数不变:同类型对象的简单赋值不会引起计数改变,结果是两个对象指向相同的内存地址,也就具有相同的计数值,包括计数为0的状态。

下面这段代码很清楚的反应了上面的5个状态。

NSObject * obj = [[NSObject alloc] init]; //obj ref count = 1

NSObject * obj1 = obj; //obj1 ref count = 1

[obj retain]; //obj ref count = 2, obj1 ref count =2

[obj release]; //obj ref count = 1, obj1 ref count = 1

[obj release]; //obj ref count = 0, obj1 ref count = 0

NSObject * obj2 = obj; //obj2 ref count = 0

虽然最后一句看起来我们不会做,但是实际复杂代码中经常会出现不知情的指向了一个引用计数已经为0对象的情况。为什么会出现这种情况,大多数是由于代码中很多隐式引用计数操作引起的。

和上面的类似,我们总结一下引用计数的隐式操作过程,这个也是我们在开发构成中要谨慎细心留意的,特别是在自定义的类型的实现中。

1. 计数置1:所有通过间接调用alloc方法返回对象实例的方法都会将新产生对象的引用计数置1。包括:继承于NSObject的类型(包括Foundation提供的类型和我们自定义的类型)都提供了很多构造alloc和初始化结合的便捷方法,那么它们都会将对象的引用计数置1。比如NSArray的arrayWithObject,NSMutableString的stringWithString等等;对象的深拷贝产生的新对象也是直接或者间接的调用了alloc,那么它的计数也被置1。

2. 计数加1: 所有通过间接调用retain的方法都会将对象的引用计数加1。包括:所有Foundation定义的集合类型(Array, Dictionary, Set等)的添加新元素的方法,比如addObjectAtIndex等都会将添加进入的对象的引用计数加1,实际它们是给该对象发送了retain消息。自己定义的集合类型应该遵守这个约定。

3. 计数减1: 所有通过间接调用release的方法都会将对象的引用计数减1。包括:所有Foundation定义的集合类型的删除对象元素的方法,比如removeObjectAtIndex等都会把对象的引用计数减去1,集合自己release的时候也会给每个元素对象发送release消息以使得元素对象引用减1;此外自动释放池也会在drain的时候给注册到它内部的每个对象发送release消息,以使得对象的引用计数减1。自己定义的集合类型也应该遵守这个约定。

下面描述一下无效对象引用的表现:

计数为0时候OC系统的动作是自动触发的,因为计数为0意味着该对象没有人引用了,那么就可以触发dealloc了,dealloc中必须完成该对象占有资源的释放,然后系统会回收为该对象分配的内存,此时,对象的引用都变成无效的了。需要强调的是,当对象的引用无效后,在不知情的情况下依然通过该引用操作对象的结果是不确定的,因为系统回收了为该对象分配的内存后,不代表该内存被立即被其他数据占用或破坏,所以在某些情况下,会出现对象看起来还能正常工作的现象。好的习惯是,在某个地方release了对象后,将此处引用重新赋值为nil,这样避免继续操作这个对象引用来继续做事。

自动释放池也是个不错的概念,其实可以把方法局部变量看作在一个自动释放池中,这个池的范围是一个方法,在方法出口一定会释放这些局部变量本身占有的内存空间。OC系统的自动释放池扩展了这种方法局部变量的概念,(其他语言中,比如最新的C++和C#标准可以用花括号对{}来表达一个局部变量的生命范围,C#的using调用等,以此达到这种自动释放的效果)。

当然自动释放池和这些也是有所不同的,因为对象如果需要由自动释放池通知release,它必须首先把自己注册到当前的自动释放池中。

首先看注册对象自己到自动释放池的过程:

1. 显式标记自动释放:对对象发送autorelease消息,就会把对象注册到当前自动释放池里。

2. 隐式标记自动释放:在初始化函数实现中调用autorelease消息,这样在对象构造的时候自动将自己注册到当前自动释放池中。

来看段代码:

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSObject * obj = [[NSObject alloc] init]; //obj ref count = 1

NSArray * arr = [NSArray arrayWithObject: @"item"]; //arr ref count = 1

[arr retain];//arr ref count = 2

[obj retain];//obj ref count = 2

[arr retain];//arr ref count = 3

[obj retain];//obj ref count = 3

[arr release];//arr ref count = 2

[obj release];//obj ref count = 2

[pool drain];

  drain之后,这里arr和obj的引用次数是多少呢?实际的运行结果是arr ref count = 1, obj ref count = 2。

这个结果证明了,arr在构建的时候已经隐式把自己注册到了当前的自动释放池中了,而obj使用基本的alloc和init并没有包括这个隐式操作,所以pool在drain的时候,发送了release消息给arr,arr的引用计数就减为了1。如果我们在obj实例对象构建好后调用代码 [obj autorelease],那么obj在pool drain之后的引用计数也是1了。

所以,我们在自定义类型的初始化方法实现中应该也遵守这个规律,加入隐式的autorelease调用。

打完收工,娃睡着了,今个还可以做几个练习巩固一下这些EBOOK上的总结,然后再写一个随笔来总结实际开发中类对象的处理规律。

Object-C内存管理的理解总结的更多相关文章

  1. Objective C内存管理之理解autorelease------面试题

    Objective C内存管理之理解autorelease   Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的A ...

  2. cocos2dx 内存管理的理解

    关于引擎内存管理的细节,网上有大量的详解,这里概括一下: cocos2d-x 的世界是基于 CCObject 类构建的,所以内存管理的本质就是管理一个个 CCObject. //CCObject 内部 ...

  3. Python的内存管理 小理解

    请看下面的一段代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 origin = {'a':100,'b':[1,2,34,5]} obj_copy ={}; ...

  4. [java小笔记] 关于数组内存管理的理解

    数组是大多数编程语言都提供的一种复合结构,如果程序需要多个类型相同的变量时,就可以考虑定义一个数组,java语言的数组变量时引用类型的变量,因此具有java引用变量的特性.在使用数组之前必须对数组对象 ...

  5. Cocos2d-x 3.2 的内存管理详解

    目标读者:了解 Cocos2d-x 中的节点以及节点树,了解引用计数,了解游戏主循环等概念. 本文首先介绍 Cocos2d-x 3.2 中内存管理的作用,以及各个作用的应用.借由通俗易懂的解释来了解内 ...

  6. Linux堆内存管理深入分析(上)

    Linux堆内存管理深入分析(上半部) 作者:走位@阿里聚安全   0 前言 近年来,漏洞挖掘越来越火,各种漏洞挖掘.利用的分析文章层出不穷.从大方向来看,主要有基于栈溢出的漏洞利用和基于堆溢出的漏洞 ...

  7. effective OC2.0 52阅读笔记(五 内存管理)

    第五章:内存管理 29 理解引用计数 30 以ARC简化引用计数 总结:ARC通过命名约定将内存管理规则标准化.其他编程语言很少像OC这样强调命名.ARC通过设置全局数据结构(此数据结构的具体内容因处 ...

  8. Objective-C之集合对象的内存管理

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  9. Linux堆内存管理深入分析

    (上半部) 作者:走位@阿里聚安全 前言 近年来,漏洞挖掘越来越火,各种漏洞挖掘.利用的分析文章层出不穷.从大方向来看,主要有基于栈溢出的漏洞利用和基于堆溢出的漏洞利用两种.国内关于栈溢出的资料相对较 ...

随机推荐

  1. nginx支持pathinfo并且隐藏index.php

    How To Set Nginx Support PATHINFO URL Model And Hide The /index.php/ 就像这样 The URL before setting lik ...

  2. spring 的aop proxy 代理

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

  3. position:absolute绝对定位解读

    position:absolute绝对定位解读  摘要   用四段代码解释absolute的定位问题,进而从概念的角度切实解决html布局问题. 一.背景 常常遇到这样一些问题,很容易混淆.“浏览器屏 ...

  4. python 面向对象-笔记

    1.如何创建类 class 类名: pass class bar: pass 2.创建方法 构造方法,__init__(self,arg) obj = 类('a1') 普通方法 obj = 类(‘xx ...

  5. flexbox布局

    一.侧轴对齐伸缩项目--align-items 它充许调整伸缩项目在侧轴(也就是y轴)的对齐方式,主要包括以下几个值: flex-start/baseline:伸缩项目在侧轴起点边的外边距紧靠住该行在 ...

  6. Validate US Telephone Numbers

    function telephoneCheck(str) { // Good luck! //return true; var phone = /^1? ?(\d{3}|\(\d{3}\))[ -]? ...

  7. Python3利用BeautifulSoup4批量抓取站点图片的代码

    边学边写代码,记录下来.这段代码用于批量抓取主站下所有子网页中符合特定尺寸要求的的图片文件,支持中断. 原理很简单:使用BeautifulSoup4分析网页,获取网页<a/>和<im ...

  8. 寻找子域名的IP段

    校网网络安全检测,第一步,我们做的工作是找出学校所有的IP段.  当然,期间我们可以利用软件帮助我们扫描,但是一款软件往往是不够的,因为它全面,所以我们用了IISPutScanner,subDomai ...

  9. 微信App支付通知验签

    微信异步通知: [AcceptVerbs("POST")] public void Notify() { //编码(101-登录无效,102-账号无效,200-成功,201-失败, ...

  10. Microsoft Visual Stduio 2005 Ent安装报错解决方法

    错误:Microsoft Visual Studio 2015 Devenv : 安装时发生严重错误 安装过程第一次出现该错误时,查看了日志文件,错误提示如下: [0EEC:0EF0][2016-10 ...