从C到C++再到Obj-C内存管理学习笔记(三)

前两篇介绍了C与C++的内存管理,这一篇介绍一下Object-C的内存管理。

Object-C是C的超集,所有C语言的特性在Object-C都可以实现。

然而在内存管理上还是存在一些不同的地方。

Object-C即面向对象C语言,其大部分的类型基于Cocoa框架,常见的有NS开头类型。

所以Object-C中大部分类型也都是以类为基础的。

Object-C中类存放于堆而非栈,故一般类对象定义都以指针形式,如:

<span style="font-size:18px;">NSString * str = @"test";</span>

而非C++形式的自动变量类型:

<span style="font-size:18px;">NSString test; //error</span>

Object-C中也存在非类类型non-Object类型,诸如CGRect,CGPoint等以C语言的结构体实现的类型,

按照C的管理规则,这些类型在局部代码块中也属于自动变量的范畴。

Object-C是C的超集,可能由于我看的资料比较少,在目前看过的Object-C资料看来,比较少提到类似C所提到的内存管理关键字,

内存区分类等。不过既然是C的超集,那么不管是关键字还是内存区分类,应该都是大同小异,这一点有哪位大神熟知的麻烦在下面评论区告知我一下。

下面引用前一篇的内存区分类图片来说明Object-C的内存分区:

由于在前一篇中介绍过各区的作用,这里不再赘述。

Object-C中的const存在常量区,extern,static还是存储于静态存储区,C中的五大存储类中的静态变量依然可以在Object-C中延续使用,如:

<span style="font-size:18px;">static CGFloat coordinatorX = 0.6;
extern NSInteger sum = 2;

//函数中
static Bool flag = 1;</span>
<span style="font-size:18px;">static const float = 2.0;</span>

non-Object 的自动变量还是存储于栈,如:

<span style="font-size:18px;">viewDidLoad {

NSInteger num = 1;
CGFloat x = 2;
CGFloat y = 3;

}</span>

而类变量存储于堆中。

Object-C在早期曾使用类似GarbegeCollection的垃圾回收机制来管理内存,不过后来逐渐被MRC取代,现在又被ARC取代。

上面提到Object-C中的对象都是放堆里,也是动态生成,这一点是Object-C与C在内存管理上最大的区别和Object-C 的特点。

而GarbegeCollection,MRC,ARC,便是用来对对象的管理,生成和释放,就如C语言中的malloc()-free()以另一种形式管理。

MRC与ARC

Object-C的内存管理中引入了引用计数retainCount的概念,顾名思义就是有多少个指针指向当前对象,当引用计数为0时,对象被释放。

MRC与ARC都基于Runtime动态实现,引用计数+1、-1,对象释放消息的发送都由Runtime来实现。

MRC(Manual Reference Counting)

MRC翻译成中文就是手动引用计数,在MRC中和C语言一样也有一些关键字,一般用于属性声明。

主要有几个,retain,assign,copy,release。

注意,下面举例代码都是在MRC环境下的!!!

Retain

当用retain 声明属性时,如:

<span style="font-size:18px;">@property (retain) NSString *name;</span>

那么当我们把一个字符串对象赋给name时,字符串的引用计数retainCount 会 +1。

也可以在生成对象时直接用,如:

<span style="font-size:18px;">NSString *temp = [[NSString stringWithString:@"hello world"] retain];</span>

这样子对象引用计数也 +1。

在accessMethor中的实现是这样的:

<span style="font-size:18px;">- (void)name:(NSString *)name {
if(self.name != name) {
[self.name release];
self.name = [name retain];
}
}</span>

Assign

assign与retain相反,用于属性声明时,只是简单的赋值,引用计数不变,如:

<span style="font-size:18px;">@property (assign) NSInteger *age;</span>

且一般也只用于简单数据类型,如NSInteger,NSUInteger等。

在accessMethor中的实现是这样的:

<span style="font-size:18px;">- (void)age:(NSInteger *)age {
self.age = age;
}</span>

需要注意的是,assign所指向对象被释放时assign的对象并不知道,这是如果使用的话会crash,称野指针。

copy

copy顾名思义就是赋值,在进行对象赋值时,复制新的对象进行赋值,原对象不变,如:

<span style="font-size:18px;">@Property (copy) NSString *str;

NSString *temp = @"hello world";

self.str = temp;</span>

这时候temp的对象引用计数不变,,新的对象被赋给str,引用计数+1;

在accessMethor中的实现是这样的:

<span style="font-size:18px;">- (void)str:(NSString *)str {
if(self.str != str) {
[self.str release];
self.str = [str copy];
}
}</span>

也可和retain一样在生成对象时使用:

<span style="font-size:18px;">NSString *temp = [[NSString stringWithString:@"hello world"] copy];</span>

这样原来的对象将被自动释放,新对象赋值给temp。

release

release是引用计数减一,将一个对象从一个指针处释放使用release,如上述中的:

<span style="font-size:18px;">[self.str release];</span>

当retainCount为0时对象自动销毁。

MRC是引用计数的手动管理方式,对于程序猿来说比较麻烦,稍不注意会出现野指针和内存泄露等问题。

MRC相比起C的内存管理方式的好处是使用引用计数简化了释放过程,更加安全。

比如在C语言中如果多处引用同一个对象,一旦在某一处释放了之后,其他都变成野指针,这样是比较危险的。

而MRC的好处在于,假如使用妥当,那么对象只有在各处释放之后,引用计数减少到0时才释放,这样子便不容易出现野指针。

然而MRC在操作上还是比较困难,需要程序猿在每时每刻清除知道哪些对象该释放哪些不该释放。苹果在iOS5之后推出了ARC自动应用计数,大大方便了程序猿的工作。

ARC(Auto Reference Counting)

ARC的引入进一步方便了OC中对象的内存管理。其引入了三个新的关键字,strong,weak,unsafe_unretained。

使用这三个新的关键字,我们不需要再去手动管理引用计数,只需在声明变量的时候附上关键字就可以了。

下面介绍这三个关键字:

strong

strong和MRC中的retain作用类似,称强引用。

应用于属性声明:

<span style="font-size:18px;">@property (strong) NSString *name;</span>

在赋值时对象引用计数自动+1,当name指向其他对象时,原来的对象引用计数自动减一,不用手动release。

在临时变量也可以使用,如:

<span style="font-size:18px;">__strong NSString *str;

NSString *str1 = @"hello world";

str = str1;</span>

这时字符串对象引用计数+1。

值得注意的是临时变量默认为strong类型。

weak

weak和MRC中的assign作用相似,称弱引用。

应用于属性声明:

<span style="font-size:18px;">@property (weak) NSString *name;</span>

在赋值时对象引用计数不变,当name指向其他对象时,原来的对象自动释放,不用手动release。

在临时变量也可以使用,如:

<span style="font-size:18px;">__weak NSString *str;

NSString *str1 = @"hello world";

str = str1;</span>

这时字符串对象引用计数不变。

当str1设为nil时,str不会变成野指针,这一点和assign不同,str自动设置为nil,因此比较安全。

weak多用于消除ARC中的循环引用,比如block中的循环引用,或用于delegate。

unsafe_unretained

unsafe_unretained与weak十分相似,都只是赋值,引用计数不变,不过unsafe_unretained顾名思义是不安全,如:

<span style="font-size:18px;">@property (unsafe_unretained) NSString *name;</span>

也可以在局部变量中加以声明,如:

<span style="font-size:18px;">__unsafe_unretained NSString *str;

NSString *str1 = @"hello world";

str = str1;</span>

当str1释放时,str就变成野指针了,这是不安全的。

值得注意的是类属性默认为unsafe_unretained类型。

相比MRC,ARC更加方便,但也存在循环引用导致内存泄露的问题,在使用时需多加留意。

小结:

Object-C是C的超集,内存管理上也存在栈,堆,静态存储区,常量区等,只不过Object-C有object变量和non-object变量之分。non-Object变量和C中的自动变量大致上相同,在局部代码块上属于自动变量,存放于栈中。而object变量便和C有一些不同,都是动态生成的,类似C中的malloc()-free(),存放于堆中。Object-C由此引入gabegeCollection,MRC,ARC等动态基于runtime的管理机制,以引用计数为基础,以retain,weak,copy,unsafe_unretained,strong,assign等关键字来管理引用计数。ARC相对MRC使用方便,不用程序猿手动释放,但也存在循环引用等缺点。

下一篇介绍Eventloop和autoreleasepool以及C,C++,Object-C的对比总结。

时间: 2024-08-10 21:14:24

从C到C++再到Obj-C内存管理学习笔记(三)的相关文章

从C到C++再到Obj-C内存管理学习笔记(二)

上一篇讲完内存管理香港概念之后,这一篇重点介绍内存堆栈.malloc()-free()a.new-delete的使用及实现过程. 首先,我们必须知道的是,每个程序在内存中分为几个存储区,静态存储区,堆栈,堆,自由存储区.常量存储区及程序二进制代码的存储区.还有就是CPU的寄存器.如下图所示: 静态存储区:存放上一篇中提到的静态变量,包括静态全局变量.静态内部变量.静态局部变量,这些变量在程序编译时被存放到静态存储区. 常量存储区:主要存放程序中涉及到的常量,比如字符串常量就是存放在这个区. 寄存

C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例

面向对象编程 --再谈文本查询示例 引言: 扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询. 为了说明问题,将用下面的简单小说来运行查询: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

C++ Primer 学习笔记_74_面向对象编程 --再谈文本查询示例[续/习题]

面向对象编程 --再谈文本查询示例[续/习题] //P522 习题15.41 //1 in TextQuery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <set> #include <map&g

Android学习笔记(十八)——再谈升级数据库

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 之前我们为了保证数据库中的表是最新的,只是简单地在 onUpgrade()方法中删除掉了当前所有的表,然后强制重新执行了一遍 onCreate()方法.这种方式在产品的开发阶段确实可以用,但是当产品真正上线了之后就绝对不行了.想象以下场景,比如你编写的某个应用已经成功上线,并且还拥有了不错的下载量.现在由于添加新功能的原因,使得数据库也需要一起升级,然后用户更新了这个版本之后发现以前程序中存储的本地数据全部丢失了.

Android学习笔记(四)——再探Intent

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们可以使用 Intent 来启动一个活动, 还可以在启动活动的时候传递数据的,下面一起来看一下: 一.向下一个活动传递数据 Intent中提供了一系列 putExtra()方法的重载,可以把我们想要传递的数据暂存在 Intent 中,启动了另一个活动后,只需要把这些数据再从Intent 中取出就可以了.例如我们可以在mainactivity中创建一个字符串,然后把它传递到second_activity里: 1)先在

js的call(obj,arg)学习笔记

var add=function (a,b){ return(a+b); } var sub=function (a,b,c){ return(a-b-c); } sub.call(add,1,2,3) //示例的意思时指用sub对象来替换add对象,并传入参数 //使用call来实现继承 function class1(){ this.name="lee"; this.say=function(){ console.log(this.name); } } function class

Android学习笔记(三四):再谈Intent(上)-一些知识

在Android学习笔记(七):多个Activity和Intent中,我们先在学会了如何使用intent在代码中唤起activity.此处作深一步地学习. 什么是Intent intent是对一个操作处理的抽象描述.context可以在使用startActivity(intent)来launch一个actvivity,就如我们在学习笔记(七)中的处理,也是最常用的方式,将activity在我们的应用中整合:可以在通过sentBroast(intent)来广播给任何有兴趣的BroadcastRec

Dynamic CRM 2013学习笔记(三十五)自定义审批流6 - 审批通过后,再审批 - 二次审批

最近有个特殊的需求,客户想做二次审批,就是审批通过后,再走一次审批流程.最开始一想,这还不简单,审批通过后,直接把状态改成draft就完了,后来一试,发现一堆问题,比如第一次审批完后,界面是不允许修改的,直接改成draft就又可以修改了:再比如审批活动记录的查找以及死循环的问题等等.于是自己动手单独写了一个公用的再审批插件,下面介绍详细的实现步骤:   一.添加字段以控制再审批的次数 添加一个字段 new_approval_count, 再审批一次就把它加1.如果只要再审批一次,那么大于1就退出

OpenCV学习笔记(四十)——再谈OpenCV数据结构Mat详解

原文:http://blog.csdn.net/yang_xian521/article/details/7107786 我记得开始接触OpenCV就是因为一个算法里面需要2维动态数组,那时候看core这部分也算是走马观花吧,随着使用的增多,对Mat这个结构越来越喜爱,也觉得有必要温故而知新,于是这次再看看Mat. Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存,对于一些大型的开发,有时候投入的lpImage内存管理的时间甚至比关注算法实现的时间还要多,这