当前位置: 首页 > news >正文

IOS内存管理

IOS内存管理

很多童鞋对于IOS中的内存管理处理的不好,有时候感觉自己已经掌握了,其实用起来的时候往往出现很多的内存泄露或者因为内存管理的不恰当导致程序崩溃!
网上有一片很详细的内存管理文章,很多人估计嫌长,不愿意去看,我就写一个简单易懂的分享。
我分四块来讲解,这篇文章比较简单,因为我不想把你们搞晕,其实总的来说就一个原则!
一、IOS内存管理的机制
IOS中没有垃圾回收机制(IOS5中好像已经有了,但是目前还不太实用),所以基本一切都是自己手动管理。
IOS中采用引用计数的内存管理方式,啥意思呢?讲的通俗点就是说一块内存地址是否应该被释放是又retaincount来决定的,如果这块内存地址的retaincount为0,那么这块内存就会被释放,再也找不到了。
所以想让他一直存在,那么它的retaincount就必须大于0。
那么如何管理呢?其实原理非常简单,就是谁用谁retain,谁retain的谁release,遵循这个原则的目的就是让你不会出错。
举个例子

ClassA *obj1 = [[ClassA alloc] init];

这里你申请了一块内存地址,指针obj1指向了这块内存地址,同时你给它retain了一下(retain,copy,alloc这几个方法都相当于把retaincount加一),此时obj1所指向的这块内存地址retaincount为1。
接着来(这里我只是举例子让大家了解一下,这些代码没有什么实际意义)
ClassA *obj2=obj1;
[obj2 retain];

好了,大家看一下,obj1和obj2同时都指向了上面所述的内存地址,我假设这块内存为A,那么A这块内存的retaincount为2。
假如大家在之后一直在使用obj1和obj2,最后不用的时候,需要释放了,我希望大家这样来释放

[obj1 release];
[obj2 release];

而不希望大家这样来释放
[obj1 release];
[obj1 release];
//或者
[obj2 release];
[obj2 release];

为什么这样呢?上面两种释放方法没有错,都成功将A这块内存的retaincount减为0,导致A被释放了。但是你没有遵循以上所说“谁用谁retain,谁retain的谁release”。
这只是一个小例子,你可能感觉不到问题的严重性,如果你做一个项目,在其中有大量这种retain,release的话,你不遵循这种原则,你会死的很惨,没有条理最后你会被搞的很乱,找都找不到。
二、不要随便看到retaincount多就给它释放
很多朋友在学完内存管理后喜欢玩消除,不该释放的他去释放,举个例子

UIViewController *ct1=[[UIViewController alloc] init];[self.navigationcontroller pushViewController:ct1 animated:YES];[ct1 relese];

这里,初始化一个viewcontroller,然后用导航push进来,最后释放这个viewcontroller,很多人在这里操作完毕后,继续打印ct1的retaincount,发现它的retaincount不为0,就继续release,这里是非常错误的方法,直接导致程序崩溃,继续看以上原则“谁用谁retain,谁retain的谁release”,你用的时候,初始化,并retain了一次,最后你release了一次,到此结束!之后不管你发现它的retaincount为几,你都不该再release。你打印出来的retaincount并不是你retain的,而是系统框架本身retain的,由框架本身来处理,开发者不必理会。
类似还有很多,数组的addobject、addsubview等等很多方法,都是框架自己家retain的,开发者本身retain后release,不必理会它的retaincount。
另外插一句,如果是多人协同开发,这种情况最容易碰到,别人传递一个参数过来,你拿过来retain后使用,使用完毕release,发现retaincount还不是0,于是你继续release,这样你就把你同伴retain的计数给release掉了,当他下次使用的时候发现崩溃了,这种情况会让你的团队浪费无数的时间来找原因。

三、关于属性
属性在.h文件中是这样声明的

@property(nonatomic,retain) Class *obj;

括号内第二个参数代表的意义是什么呢?
我们分析一下他内部的机制
@property (retain) Class* obj;@synthesize obj;实际上是gettersetter,有retain参数的property,内部代码如下

-(Class *) getObj
{
return obj;
}


-(void) setObj:(Class *) value
{
if (obj != value)
{
[obj release];
obj = [value retain];
}
}

愿意仔细分析的童鞋可以仔细研究一下,不太明朗的童鞋请听我说。
这个属性定义以后如何使用呢?我们只说赋值(也就是set)
self.obj=xxx;
[self setObj:xxx];
obj=xxx;
赋值方法有以下几种,其中前两个是相同的,都是调用了setObj:方法同时也就导致了obj的retaincount增加了1,而第三个赋值方法并没有增加retaincount,只是将指针指向了xxx内存地址。
所以大家赋值时可以这样
Class *c=[[Class alloc] init];
self.obj=c;[c release];
或者Class *c=[[Class alloc] init];
[self setObj:c];[c release];
或者Class *c=[[Class alloc] init];obj=c;

属性的这个机制是让你在整个self里(也就是当前类的实例里)一直保留这个obj的retaincount,所以你必须在此类的dealloc里将obj的retaincount置零(你只需要release,如果你严格遵守“谁用谁retain,谁retain的谁release”的话)
-(void)dealloc
{
[obj release];
[super dealloc];
}


这里,其实我主要要说明的就是属性的赋值方式用self.xxx=xxxx和xxx=xxxx是完全不同的赋值方式,大家切记!
四、autorelease对象
autorelease对象就是自动释放对象,大家可能会疑惑,能自动释放,我还管理干嘛,我都用自动释放算了,事实不是这样的,自动释放确实好用,但是自己管理内存才能让项目占用更小的资源,跑起来更流畅,大家可以手动管理加autorelease一起来使用,我先讲解一下autorelease对象到底是什么个情况。
首先,告诉大家autorelease对象什么时候才会被释放。
在main函数里有一个autorelease pool,自动释放池,所以在这个自动释放池里的autorelease对象都会在自动释放池结束的时候全部被释放。
大家可能会问了,“难道所有的autorelease对象都是在程序退出的时候才被释放?”,答案当然不是,其实main函数中只是一个程序中众多自动释放池中的一个,每个runloop都会隐性的创建一个自动释放池,啥是一个runloop?每个UIView创建、delegate回调等等都会创建一个自动释放池,记得这个“等等”,意思就是有很多(如果大家想要详细了解runloop,可以去google)。所以大家不用担心autorelease对象不会被释放。
很多系统的方法,比如[NSArray array]、[NSString stringformat]等等返回的对象都是autorelease对象,这些对象是不需要手动释放的,如果手动释放会导致程序崩溃!切记!,原则就是系统中所有没有使用alloc、copy、retain来创建的对象都是autorelease对象,大家千万别手动release!


好了,就说这么多吧,如果有什么错误,希望大家提出,我来改。。。工作太忙,也没啥时间写的更详细!!!望大家见谅!

相关文章:

  • gerrit + ldap + phpldapadmin docker部署
  • 【编程之美】2.1 - 求二进制数中1的个数
  • JavaScript中数组的排序方法:1.冒泡排序 2.选择排序
  • js计算页面加载时间
  • Solium代码测试框架
  • 迎接第五次工业革命浪潮,不当纳米知识文盲
  • 12-单表查询
  • Microsoft Component Designer 设计组件一例
  • 百度云高速下载Pandownload
  • CF卡格式化XPE启动盘
  • BZOJ 3224: Tyvj 1728 普通平衡树 or 洛谷 P3369 【模板】普通平衡树-Splay树模板题
  • Linux 抓取网页实例(shell+awk)
  • 计算机网络--TCP三次握手和四次挥手
  • 纳米技术是云计算的大救星
  • set集合的常用方法
  • 分享的文章《人生如棋》
  • Angular2开发踩坑系列-生产环境编译
  • ES6之路之模块详解
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Spring Boot快速入门(一):Hello Spring Boot
  • 初识 webpack
  • 从PHP迁移至Golang - 基础篇
  • 简析gRPC client 连接管理
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 你对linux中grep命令知道多少?
  • ionic入门之数据绑定显示-1
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2015)JS ES6 必知的十个 特性
  • (arch)linux 转换文件编码格式
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (Ruby)Ubuntu12.04安装Rails环境
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)计算机毕业设计SSM保险客户管理系统
  • (十)T检验-第一部分
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)使用VMware vSphere标准交换机设置网络连接
  • (转)树状数组
  • **PHP分步表单提交思路(分页表单提交)
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .net 7 上传文件踩坑
  • .net CHARTING图表控件下载地址
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .Net Web项目创建比较不错的参考文章
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • :=
  • @EnableAsync和@Async开始异步任务支持
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • [Android] 修改设备访问权限
  • [BSGS算法]纯水斐波那契数列
  • [C进阶] 数据在内存中的存储——浮点型篇
  • [C语言][C++][时间复杂度详解分析]二分查找——杨氏矩阵查找数字详解!!!
  • [E单调栈] lc2487. 从链表中移除节点(单调栈+递归+反转链表+多思路)
  • [HCTF 2018]WarmUp (代码审计)