iOS内存管理

一.内存管理 /引用计数

Objective-C 中的内存管理,也就是引用计数

1.1内存管理的思考方式

  • 自己生成的对象,自己持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放
    注意: "自己"是指"对象的使用环境",以上内存管理思考方式在MRC与ARC下都是可行的,只是源代码的记述方法稍有不同,可以从后文中所述ARC中追加的所有权声明开始理解。
演示文稿.001.jpeg

在objective-c内存管理中,"生成","持有","释放"和"废弃"四个词频繁出现,这些内存管理方法不包括在该语言中,而是由Cocoa框架中Foundation框架类库的NSObject类担负内存管理的职责

框架与类的关系.jpeg

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

使用以下名称开头的方法意味着自己生成的对象只有自己持有

  • alloc
  • new
  • copy
  • mutableCopy
    此外,使用驼峰写法(CamelCase)命名的下列名称也意味着自己生成并持有对象
  • allocMyObject
  • newThatObject
  • copyThis
  • mutableCopyYourObject

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

用上述之外方法取得的对象,因为非自己生成并持有,所以自己不是该对象的持有者,如NSMutableArray类的 array类方法。
通过使用retain方法,非自己生成的对象跟用第一类方法生成并持有的对象一样,成为了自己所持有的。

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

自己持有的对象,一旦不再需要,持有者有义务释放该对象,释放使用release方法。

  • 用alloc方法由自己生成并持有的对象通过release方法释放

  • 自己生成而非自己持有的对象,若用retain方法变为自己持有,同样可以用release方法释放

  • 用某个方法生成对象,并将其返还给该方法的调用方,这种情况下:

    • 原封不动地返回alloc方法生成并持有的对象,就能让调用方也持有该对象(被调用方法名符合alloc等方法命名规则)

      - (id)allocObject {
      //自己生成并持有对象
      id obj =[ [NSObject alloc] init];
      //自己持有对象
      return obj;
      }
      // 取得非自己生成并持有的对象
      id obj1 = [ obj0 allocObject];
      自己持有对象
      
  • 调用 [NSMutableArray array]方法取得的对象存在,但自己不持有对象,又是如何实现的呢
    - (id)object {
    id obj = [[NSObject alloc] init];
    //自己持有对象
    [obj autorelease];
    //取得的对象存在,但自己不持有对象
    return obj;
    }
    id obj1 = [obj0 object];
    //取得的对象存在,但自己不持有对象,当然也能够通过retain方法调用autorelease方法取得的对象变为自己持有,如下
    [obj1 retain];
    //自己持有对象
    autorelease提供这样的功能,使对象在超出指定的生存范围时能够自动并正确的释放

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

  • 自己生成并持有对象后,在释放完不再需要的对象之后再次释放
  • 取得的对象存在,但自己不持有对象时释放

1.2 autorelease

看上去很像ARC,实际上更类似C语言中自动变量(局部变量)的特性:

{
    int a;
}
 //因为超出变量作用域,自动变量"int a"被废弃,不可再访问

autorelease会像c语言自动变量这样来对待对象实例。当超出其作用域时,对象实例的release实例方法被调用。不同的是,可以设定变量的作用域。

autorelease具体使用方法如下:
  • 生成并持有NSAutoreleasePool对象;
  • 调用已分配对象的autorelease实例方法;
  • 废弃NSAutoreleasePool对象。

NSAutoreleasePool对象的生命周期相当于c语言变量的作用域。对于所有调用过autorelease实例方法的对象,在废弃NSAutoreleasePool对象时,都将调用release实例方法。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];   //等同于[obj release];

在Cocoa框架中,相当于程序主循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成、持有和废弃处理。所以应用程序开发者不一定非得使用NSAutoreleasePool对象来进行开发工作。


NSRunLoop每次循环过程中NSAutoreleasePool对象被生成货废弃.jpeg

尽管如此,在大量产生autorelease对象时,只要不废弃NSAutoreleasePool对象,那么生成的对象就不能被释放,因此有时会产生内存不足的现象。典型的例子时读入大量图像的同时改变其尺寸。图像文件读入到NSData对象,并从中生成UIImage对象,改变该对象尺寸后生成新的UIImage对象,这种情况下会大量产生autorelease对象。

for(int i = 0; i<图像数;++i) {
    /*
     *读入图像
     *大量产生autorelease的对象
     *由于没有废弃NSAutoreleasePool对象
     *最终导致内存不足
    */
}

在此情况下,有必要在适当的地方生成、持有或废弃NSAutoreleasePool对象。

for(int i = 0; i<图像数;++i) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 /*
  *读入图像
  *大量产生autorelease的对象
  */
  [pool drain];
 /*
  *通过[pool drain],
  *autorelease  
  */
}

二. ARC规则

实际上"引用计数式内存管理"的本质部分在ARC中并没有改变,ARC只是自动地帮助我们处理"引用计数"的相关部分。

2.1所有权修饰符

ARC有效时,id类型和对象类型同c语言其他类型不同,其类型上必须附加所有权修饰符。所有权修饰符一共有4种。

  • __strong修饰符
  • __weak修饰符
  • __unsafe_unretained修饰符
  • __autoreleasing修饰符

2.1.1 __strong修饰符

_ _strong修饰符是id类型和对象类型默认的所有权修饰符。在没有明确指定所有权修饰符时,id和对象类型默认为__strong修饰符。

以下两种代码表意相同
id obj = [[NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];

如"strong"名称所示,附有 _ _strong修饰符表示对对象的"强引用",附有 __strong修饰符的变量obj在超出其变量作用域时,即在该变量被废弃时,会释放其被赋予的对象,如下:

1. 自己生成并持有对象
{
    //自己生成并持有对象
    id __strong obj = [[NSObject alloc] init];
    //因为变量obj为强引用,所以自己持有对象
}
    //因为变量obj超出其作用域,强引用失效
    //所以自动地释放自己持有的对象。
    //对象的所有者不存在,因此废弃该对象

2.取得非自己生成并持有的对象
{
    //取得非自己生成并持有的对象
    id __strong obj = [NSMutableArray array];
    //因为变量obj为强引用,所以自己持有对象   
}
    //因为变量obj超出其作用域,强引用实效,
    //所以自动地释放自己持有的对象

附有__strong修饰符的变量之间可以相互赋值。
举例暂略
即便是objective-c类成员变量,也可以在方法参数上,使用附有 _strong修饰符的变量
举例暂略
另外,__strong修饰符通后面要讲的__weak修饰符合__autorelease修饰符一起,可以保证奖附有这些修饰符的自动变量初始化为nil。

2.1.2 __weak修饰符

仅通过__strong修饰符是不能解决有些重大问题的,带有__strong修饰符的成员变量在持有对象时,很容易发生循环引用。循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生存期后继续存在。
使用__weak修饰符可以避免循环引用。__weak修饰符与__strong修饰符相反,提供弱引用。弱引用不能持有对象实例,所以在超出其变量作用域时,对象即被释放。
举例待补。。。
__weak修饰符还有另一有点,在持有某对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态,通过检查附有__weak修饰符的变量是否为nil,可以判断被赋值的对象是否已废弃。

2.1.3__unsafe_unretained修饰符

__unsafe_unretained修饰符正如其名unsafe所示,是不安全的所有权修饰符。尽管ARC式的内存管理是编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。
附有__unsafe_unretained修饰符的变量同附有_weak修饰符的变量一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会立即被释放。
举例很重要,待补充。。。
在使用__unsafe_unretained修饰符时,赋值给附有__strong 修饰符的变量时有必要确保被赋值的对象却是存在。使用__unsafe_unretained修饰符变量的必要性:在iOS4的应用程序中,必须使用__unsafe_unretained修饰符来替代__weak修饰符。赋值给附有__unsafe_unretained修饰符变量的对象在通过该变量使用时,如果没有确保其确实存在,那么应用程序就会崩溃。

2.1.4__autorealeasing修饰符

ARC有效时

  • 不能使用autorelease方法

  • 不能使用NSAutoreleasePool类
    这样以来,虽然autorelease无法直接使用,但ARC有效时autorelease功能是起作用的。

    //ARC无效
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    id obj = [[NSObject alloc] init];
    [obj autorelease];
    [pool drain];
    
     //ARC有效时,该源码能写成下面这样:
     @autoreleasepool {
            id __autoreleasing obj = [[NSObject alloc] init];
     }
    

指定"@ autoreleasepool块"来替代"NSAutoreleasePool"类对象生成、持有即废弃"这一范围,但是,显式地附加__autoreleasing修饰符同显式地附加__strong修饰符一样罕见,autoreleasing不同于autorelease,它用于按引用传递对象
@ autoreleasepool图片表述待补
@ autoreleasepool四个非显式使用_autoreleasing修饰符的例子

2.2规则

  • 不能使用retain/release/retainCount/autorelease
  • 不能使用NSAllocateObject/NSDeallocateObject
  • 须遵守内存管理的方法命名规则
  • 不要显式调用dealloc
  • 使用@autoreleasepool块替代NSAutoreleasePool
  • 不能使用区域(NSZone)
  • 对象型变量不能作为c语言结构体(struct/union)的成员
  • 显式转换"id"和"void*"
2.2.1 不能使用retain/release/retainCount/autorelease
2.2.2 不能使用NSAllocateObject/NSDeallocateObject
2.2.3 须遵守内存管理的方法命名规则

在ARC无效时,用于对象生成/持有的方法必须遵守以下命名规则
alloc/new/copy/mutableCopy,上述名称开始的方法在返回对象时,必须返回给调用房所应当持有的对象。这在ARC有效时也一样,返回的对象完全没有改变。只是在ARC有效时要追加一条命名规则。

  • init
    以init开始的方法的规则要比alloc/new/copy/mutableCopy更严格。该方法必须时实例方法,并且必须要返回对象。返回的对象应为id类型或该方法声明类的对象类型,抑或是该类的超类型或子类型。该返回对象并不注册到autoreleasepool上。基本上只是对alloc方法返回值的对象进行初始化处理病返回该对象。
    以init开始的方法的命名规则:

    • (id)initWithObject:(id)obj; //可以
    • (void)initThisObject; //遵守了命名规则,但虽然以init开始,却没有返回对象,不能使用
    • (void)initialize; //不包含在命名规则里
2.2.4 不要显式调用dealloc

对象被废弃时,不管ARC是否有效,都会调用对象的dealloc方法
dealloc方法在大多数情况下还适用于删除已注册的代理或观察者对象

2.2.5 使用@autoreleasepool块替代NSAutoreleasePool
2.2.6不能使用区域(NSZone)
2.2.7对象型变量不能作为c语言结构体(struct/union)的成员
2.2.8显式转换"id"和"void*"

2.3 属性

属性时在Objective-c2.0引入的,提供了一种轻松地为类声明实例变量的方式,并让编译器自动生成存取方法。
当ARC有效时,以下可作为这种属性声明中使用的属性来用

属性声明的属性与所有权修饰符的对应关系.jpeg

2.4数组

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容