详解关于iOS内存管理的规则思考

关于iOS内存管理的规则思考

自己生成的生成的对象,自己持有。

非自己生成的对象,自己也能持有。

不在需要自己持有的对象时释放。

非自己持有的对象无法释放。

注:这里的自己是对象使用的环境,理解为编程人员本身也没有错

对象操作和Objective-C方法对应

对象操作 Objectivew-C方法
生成并持有对象 alloc/copy/mutableCopy/new或以此开头的方法
持有对象 retain
释放对象 release
废弃对象 dealloc

自己生成的对象,自己持有

//自己生成并持有对象
id obj1 = [[NSObject alloc] init];

id obj2 = [NSObject new];

id obj3 = [obj2 copy];

copy方法基于NSCopying方法约定,实现类中的copyWithZone:

mutableCopy方法基于NSMutableCopying方法约定,实现类中的mutableCopyWithZone:

非自己生成的对象,自己也能持有

用alloc/new/copy/mutableCopy以外的方法取得的对象,自己不是该对象的持有者。

//取的非自己生成并持有的对象,
//取得对象的存在,但自己不持有对象。

id obj = [NSMutableArray array];

id obj2 = [NSDictionary dictionary];

//自己持有对象
[obj retain];

[obj2 retain];

注:这里有点不好理解,我们先来看一段代码:

//取的非自己生成并持有的对象,
//取得对象的存在,但自己不持有对象。

id unretain_obj = [NSMutableArray array];

NSLog(@"unretain_obj retain count = %lu", (unsigned long)[unretain_obj retainCount]);
//调用 release
[unretain_obj release];

上述代码,我们打印结果是:

2016-12-21 15:32:04.485 acm[65216:852108] unretain_obj retain count = 1

随后调用release方法会导致程序崩溃!

按照引用计数来说,这时unretain_obj是可以被执行一次release方法的。但是为什么我们直接调用会导致程序崩溃。

我们会想最开始提到的四条思想之一:

无法释放非自己持有的对象

这样我们就很好理解了。虽然打印出unretain_obj的retainCount 为 1 但是不能说明是因为它引用了对象。它只是单纯的获取到了对象的存在而已。

那么我们会产生一个问题。那么这个对象是谁在持有??

我们先做一个猜测:

因为[NSMutableArray array]是一个工厂方法,在array肯定是要生成一个NSMutableArray实例对象。这时也必然会有一个指针引用它然后返回这个对象。so。。。

先想到这里,后边我们再去印证

我们再来看一段代码:

//取的非自己生成并持有的对象,
//取得对象的存在,但自己不持有对象。
id unretain_obj = [NSMutableArray array];

NSLog(@"unretain_obj retain count = %lu", (unsigned long)[unretain_obj retainCount]);

//自己持有对象
[unretain_obj retain];

NSLog(@"unretain_obj retain count = %lu", (unsigned long)[unretain_obj retainCount]);

//释放自己持有的对象
[unretain_obj release];

NSLog(@"unretain_obj retain count = %lu", (unsigned long)[unretain_obj retainCount]);

打印结果

2016-12-21 15:40:20.774 acm[65682:861135] unretain_obj retain count = 1
2016-12-21 15:40:20.774 acm[65682:861135] unretain_obj retain count = 2
2016-12-21 15:40:25.254 acm[65682:861135] unretain_obj retain count = 1

并且程序也不会崩溃。

着也印证了我们上边的想法。

因为通过retain方法,非自己生成的对象跟用alloc/new/copy/mutableCopy方法生成并持有的对象一样,成了自己所持有的

不在需要自己持有的对象时释放

通过上边的例子我们知道,自己持有的对象在释放时调用release方法,eg:

//自己生成并持有对象
id release_obj = [[NSObject alloc] init];

//将自己持有的对象释放
[release_obj release];

/*
 * 释放对象
 * 指向对象的指针依然被保留在变量release_obj 中,你依然可以调用它。
 * 但是对象一经释放绝对不可访问,否则会造成程序崩溃。
 * 出现EXC_BAD_ACCESS Crash问题
 */

我们自己实现一个方法,返回一个方法调用着也可以持有的对象,即alloc的作用

- (id)allocObject {
   //自己生成并持有对象
  id obj = [[NSObject alloc] init];
  //原封不动的返回一个由alloc方法生成的对象
  return obj;

注:方法名符合 生成并持有对象  alloc/copy/mutableCopy/new或以此开头的方法 规则

我们自己实现一个方法,返回一个谁也不持有的对象,只是取得对象的存在

- (id)object {
  //自己生成并持有对象
  id obj = [[NSObject alloc] init];

  //调用autorelease方法 取得对象的存在,但自己不持有对象。
  [obj autorelease];

  return obj;

autorelease方法可以取得对象的存在,但自己不持有对象。使对象在超出指定的生存范围时能够自动的并正确的释放(调用release方法)

autorelease和release方法的区别

autorelease:

release:

autorelease的详细解说我们后边介绍。

我们也可以通过调用retain方法来使 autorelease方法的来的对象自己持有eg:

//获取对象的存在,自己不持有
 id unretain_obj = [NSMutableArray array];

 //持有对象
[unretain_obj retain];

无法释放非自己持有的对象

自己已经释放了还继续释放

  //自己生成并持有对象
  id release_obj = [[NSObject alloc] init];

  //将自己持有的对象释放
  [release_obj release];

  //释放已经释放的对象
  [release_obj release];

  /*
   * 释放对象
   * 指向对象的指针依然被保留在变量release_obj 中,你依然可以调用它。
   * 但是对象一经释放绝对不可访问,否则会造成程序崩溃。
   * 出现EXC_BAD_ACCESS Crash问题
   */

只获取了对象的存在,试图释放对象

  //取的非自己生成并持有的对象,
  //取得对象的存在,但自己不持有对象。
  id unretain_obj = [NSMutableArray array];
  //释放自己不持有的对象
  [unretain_obj release];

程序崩溃,报EXC_BAD_ACCESS Crash问题

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 剖析iOS开发中Cocos2d-x的内存管理相关操作

    一,IOS与图片内存 在IOS上,图片会被自动缩放到2的N次方大小.比如一张1024*1025的图片,占用的内存与一张1024*2048的图片是一致的.图片占用内存大小的计算的公式是:长*宽*4.这样一张512*512 占用的内存就是 512*512*4 = 1M.其他尺寸以此类推.(ps:IOS上支持的最大尺寸为2048*2048). 二,cocos2d-x 的图片缓存 Cocos2d-x 在构造一个精灵的时候会使用spriteWithFile或者spriteWithSpriteFrameNa

  • IOS中内存管理那些事

    Objective-C 和 Swift 语言的内存管理方式都是基于引用计数「Reference Counting」的,引用计数是一个简单而有效管理对象生命周期的方式.引用计数分为手动引用计数「ARC: AutomaticReference Counting」和自动引用计数「MRC: Manual Reference Counting」,现在都是用 ARC 了,但是我们还是很有必要了解 MRC. 1. 引用计数的原理是什么? 当我们创建一个新对象时,他的引用计数为1: 当有一个新的指针指向这个对象

  • shell脚本监控linux系统内存使用情况的方法(不使用nagios监控linux)

    一.安装linux下面的一个邮件客户端msmtp软件(类似于一个foxmail的工具) 1.下载安装: 复制代码 代码如下: # tar jxvf msmtp-1.4.16.tar.bz2# cd msmtp-1.4.16# ./configure --prefix=/usr/local/msmtp# make# make install 2.创建msmtp配置文件和日志文件(host为邮件域名,邮件用户名test,密码123456) 复制代码 代码如下: # vim ~/.msmtprcacc

  • iOS通过逆向理解Block的内存模型

    前言 正常情况下,通过分析界面以及 class-dump 出来头文件就能对某个功能的实现猜个八九不离十.但是 Block 这种特殊的类型在头文件中是看不出它的声明的,一些有 Block 回调的方法名 dump 出来是类似这样的: - (void)FM_GetSubscribeList:(long long)arg1 pageSize:(long long)arg2 callBack:(CDUnknownBlockType)arg3; 因为这种回调看不到它的方法签名,我们无法知道这个 Block

  • 解析iOS内存不足时的警告以及处理过程

    内存警告 ios下每个app可用的内存是被限制的,如果一个app使用的内存超过了这个阀值,则系统会向该app发送Memory Warning消息.收到消息后,app必须尽可能多的释放一些不必要的内存,否则OS会关闭app. 几种内存警告级别(便于理解内存警告之后的行为)  Memory warning level: 复制代码 代码如下: typedef enum {                      OSMemoryNotificationLevelAny      = -1,     

  • IOS 调整内存中的图片大小实例详解

    IOS 调整内存中的图片大小实例详解 在从网路download图片,或者从相册读取图片的时候,如果ImageView的本身就是固定的300*200,那么载入2000*2000的图片是很浪费内存的. 2000*2000的内存占用是2000*2000*4bit 以下两个函数可以用来创建一个新的按照固定大小的图片.简单来说,就是Core Graphics来创建一个bitmap,然后生成一个图片. - (UIImage*)imageWithImage:(UIImage*)image scaledToSi

  • 详解iOS应用开发中的ARC内存管理方式

    提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是"本地变量" 零.简介 ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain.release.autorelease语句.你不再需要担心内存管理,因为编译器为你处理了一切 注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是类似于其它语言中的垃圾收集器.因此 ARC

  • 详解使用Xcode7的Instruments检测解决iOS内存泄露(最新)

    作为一名iOS开发攻城狮,在苹果没有出ARC(自动内存管理机制)时,我们几乎有一半的开发时间都耗费在这么管理内存上.后来苹果很人性的出了ARC,虽然在很大程度上,帮助我们开发者节省了精力和时间.但是我们在开发过程中,由于种种原因,还是会出现内存泄露的问题.内存泄露是一个很严重的问题.下面就简单介绍下怎么使用Xcode7自带的Instruments中的Leaks检测我们的程序有没有内存泄露和定位内存泄露的代码.(分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的)

  • IOS 常见内存泄漏以及解决方案

    IOS 常见内存泄漏以及解决方案 整理了几个内存泄漏的例子,由于转载地址已经找不到了,在这里就不一一列出来了. 1 OC和CF转化出现的内存警告 CFStringRef cfString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)picDataString,NULL,CFSTR(":/?#[]@!$&'()*+,;="),kCFStringEncodingUTF8); N

  • iOS内存错误EXC_BAD_ACCESS的解决方法

    iOS开发,最郁闷的莫过于程序毫无征兆地就崩溃了,用bt命令打出调用栈,给出的是一堆系统EXC_BAD_ACCESS的信息,根本没办法定位问题出现在哪里. 首先说一下 EXC_BAD_ACCESS 这个错误,可以这么说,90%的错误来源在于对一个已经释放的对象进行release操作.举一个简单的例子来说明吧,首先看一段Java代码: 复制代码 代码如下: public class Test{ public static void main(String[] args){ String s = "

随机推荐