OC语言中的内存管理机制为ARC(Automatic Reference Counting,自动引用计数)。于2011年中旬推出,替换陈旧且低效的手动内存管理,关于手动内存管理的内容,本章教程不再讲授。本章主要从以下几个方面对内存管理进行展开讲解。

  • 内存管理原则
  • 对象引用类型
  • 属性引用类型
  • 强引用循环
  • AUTO类型与释放池

1.内存管理原则

核心原则:没有被对象指针使用(指向)的内存立即释放。这个原则决定一般情况下,不会有内存泄露的情况存在,但存在特殊情况,也是本章最后一个专题要阐述的问题。

内存泄露是指,一块没有被指针引用的内存,但由于一些原因,无法释放,造成内存浪费的情况。

管理原则

  • 强引用对象指针使用中的内存绝对不会释放。
  • 归零弱引用对象指针使用中的内存,释放情况由其他对象指针决定,本身不影响内存释放与否,但其指向的内存一旦释放,本身立即置nil,保证不出现野指针。
  • 弱引用对象指针使用中的内存,释放情况由其他对象指针决定,本身不影响内存释放与否,但其指向的内存一旦释放,本身值不会发生改变,会出现野指针的情况。
  • AUTO类型对象指针使用过或使用中的内存,出释放池才会释放。通过AUTO类型与释放池配合使用,可以精确调节内存时间,提前或延后。

2.对象引用类型

对象引用类型有如下四种

  • 强引用:__strong修饰的对象指针或无修饰的对象指针
  • 归零弱引用:__weak修饰的对象指针
  • 弱引用:__unsafe__unretain修饰的对象指针
  • AUTO类型:__autoreleasing修饰的对象指针

首先分析强引用对象指针的使用情况。在分析内存释放情况时,我们需要一个测试类进行释放测试。当一个对象释放时,它的dealloc方法会被调用。所以我们在dealloc方法中进行相关输出,便能精确看到,该对象何时释放。

@interface Test : NSObject@end @implementation Test - (void)dealloc { NSLog(@"该对象释放"); } @end 

情况1

int main(int argc, const char * argv[]) { { Test * t = [[Test alloc]init]; } //代码运行至此,t的作用域结束,t指向的内存并无其他对象指针使用,所以,该内存在此释放。 return 0; } 

情况2

int main(int argc, const char * argv[]) { { Test * t1; { Test * t = [[Test alloc]init]; t1 = t; } //代码运行至此,t作用域结束,但t指向的内存仍有t1对象指针使用,所以在此该内存不会释放。 } //代码运行至此,t1作用域结束,t1指向的内存再无其他对象指针使用,所以在此内存释放。 return 0; } 

情况3

int main(int argc, const char * argv[]) { { Test * t = [[Test alloc]init]; t = nil;//代码运行至此,t不再指向之前分配的内存,之前的内存无对象指针使用,释放。 } return 0; } 

以上是强引用的常用情况,其对象指针并无任何修饰符进行修饰,但已经OC语法规定,对象指针无修饰时,为强引用类型。

我们继续讨论归零弱引用类型的对象指针对内存的影响。

情况1

int main(int argc, const char * argv[]) { { __weak Test * t1; { Test * t = [[Test alloc]init]; t1 = t; } //代码运行至此,t作用域结束,t指向的内存仍有t1对象指针使用,但t1为归零弱引用,不会影响对象释放情况,所以在此内存释放,且t1本身值变为nil } return 0; } 

情况2

int main(int argc, const char * argv[]) { __weak Test * t1 = [[Test alloc]init];//在此语句运行时,分配的内存并无除弱引用对象指针以外的对象指针使用,所以,该内存立即释放。 return 0; } 

以上是归零弱引用的情况,不常用。归零弱引用只有一种情况下使用:

  • 代码块回调接口中的自身代码块引用自身对象

例如:

@interface Room : NSObject
@property (strong, nonatomic) Light *lightA; @property (strong, nonatomic) SwitchB *s; @end @implementation Room - (instancetype)init { self = [super init]; if (self) { self.lightA = [[Light alloc] init]; self.s = [[SwitchB alloc] init]; __weak __block Room * copy_self = self;//打破强引用循环,强引用循环的概念下文会讲解。 self.s.changeStateBlockHandle = ^(SwitchState state) { if (state == SwitchStateOff) { [self.lightA turnOff]; } else { [self.lightA turnOn]; } }; } return self; } @end 

弱引用和归零弱引用管理内存的释放时间相同。弱引用是OC为兼容之前特殊情况下的内存管理而做的一个不常用类型。所以之后,我们不会使用弱引用,所有需要弱引用的地方全部以归零弱引用代替。

3.属性引用类型

属性的内存控制符,有四种情况。

  • strong
  • weak
  • copy
  • assgin

对于对象来说,可选的只有前三种。第四种,assgin为无内存管理,主要针对基础数据类型设计。例如

@property (assgin, nonatomic) int a;

如果一个属性是对象,那么其属性内存控制符必然是前三种之一。

strong:默认情况下使用,除特殊情况。

weak:在遇到强引用循环时,使用。

copy:在进行数值对象赋值时,使用。

例如

@property (copy, nonatomic) NSString * name;
@property (copy, nonatomic) NSNumber * age; 

但也可以strong代替,所以,copy使用场景也不多见。

4.强引用循环

在内存管理中,强引用循环是最严重的失误,会造成大量内存泄露。通过一个例子来说明为什么会产生泄露。

首先用实际生活中的一个场景来具体的说明一下,问题产生的原因。

例如,现在在一个教室,有学生有老师。老师对自己的要求是,学生不离开教室,我就不离开教室。而学生对自己的要求是,老师不离开教室,我就不离开教室。这样一直持续下去的结果就是,双方谁都不会离开教室。

然后我们再看一下代码中何时会产生这种情况。

@interface Student : NSObject@property (strong, nonatomic) Teacher *tea;
@end
@interface Teacher : NSObject@property (strong, nonatomic) Student *stu;
@end
main()
{{Teacher * tea = [[Teacher alloc] init];Student * stu = [[Student alloc] init];tea.stu = stu;stu.tea = tea;}}

上述代码中,可以发现,Student类有一个strong类型的属性tea,通过管理原则我们可以知道,stu对象存在其强引用属性tea一定存在,不会释放。同样Teacher有一个strong属性stu,tea对象存在意味着stu对象也绝对不会释放。这样当两个对象指针作用域消失时,其使用的内存无法释放,出现内存泄露。

这种问题便是内存管理中会遇到的强引用循环,也是目前能够造成内存泄露的唯一原因。需要对这样的情况在设计层面进行避免。互相包含对方类型的属性的结构中,必须有一方为归零弱引用。

目前存在双向包含的场景只有在回调中会用到

  • 目标动作回调中,储存target对象的属性为weak
  • 委托回调中,储存delegate委托人的属性为weak

除上述两种情况外,其他地方默认使用strong修饰属性即可。

5.AUTO类型与释放池

在内存管理中有一种较为特殊的类型叫AUTO类型,虽然名字和自动相关,但其释放仍需要手动配置释放池来调整。

__autoreleasing:被此种修饰符修饰的对象指针,其使用过和使用中的内存在出释放池时才会释放。所以可以通过手动配置自动释放池的位置来调节释放时间。

延迟释放的例子:

@autoreleasepool
{{__autoreleasing Student * stu = [[Student alloc] init];}//在此,stu的作用域虽然已经结束,但stu为AUTO类型,所以等代码运行到释放池结束才会释放
}
//在此位置,内存释放

提前释放的例子:

__autoreleasing Student * stu;
@autoreleasepool
{stu = [[Student alloc] init];
}
//在此位置,内存释放,虽然stu的作用域没有结束

使用AUTO的类型有两种情况

情况1为对象的便利构造器方法,需要延迟释放

+(id)student
{__autoreleasing Student * stu = [[Student alloc] init];
    return stu;
}

OC语言规定,方法返回的内存必须为AUTO类型。

情况2为在一个封闭循环内,用便利构造器创建对象

for(int i = 0;i<10000;i++) { @autoreleasepool { __autoreleasing Student * stu = [Student student]; } } 

因便利构造器返回的对象为AUTO类型,所以该对象指针使用的内存只有在出释放池时才会释放。但for循环中无释放池,这会造成,大量无用的对象无法立即释放。

添加释放池之后,内存便可以在使用结束之后立即释放。

[代码展示]

1.

======Test类的声明======

#import <Foundation/Foundation.h>

@interface Test : NSObject

@end

======Test类的实现======

#import "Test.h"

@implementation Test

//对象被释放之前要调用的方法

-(void)dealloc

{

NSLog(@"Test被释放。");

}

@end

======main======

#import <Foundation/Foundation.h>

#import "Test.h"

int main(int argc, const char * argv[]) {

//__strong 强引用,它会造成对象的引用计数器的变化(+1)

@autoreleasepool {

__strong Test *t2;

{

Test *t = [[Test alloc]init];

t2 = t;

t=nil;

NSLog(@"2");

}

NSLog(@"1");

}

NSLog(@"3");

return 0;

}

======运行结果======

2

1

Test被释放。

3

2.

======Test类的声明======

#import <Foundation/Foundation.h>

@interface Test : NSObject

@end

======Test类的实现======

#import "Test.h"

@implementation Test

-(void)dealloc

{

NSLog(@"Test被释放");

}

@end

======main======

#import <Foundation/Foundation.h>

#import "Test.h"

int main(int argc, const char * argv[]) {

//    @autoreleasepool {

//        //__weak 弱引用,不会造成引用计数器的变化,同时也不能阻止对象的释放,对象被释放后自动指向了nil

//        __weak Test *t2;

//        {

//            __strong Test *t = [[Test alloc]init];

//            t2 = t;

//            NSLog(@"%p",t2);

//            NSLog(@"1");

//        }

//        NSLog(@"%p",t2);

//        NSLog(@"2");

//    }

@autoreleasepool {

//__unsafe_unretained 与__weak相同,在对象被释放后不会指向nil

__unsafe_unretained Test *t2;

{

__strong Test *t = [[Test alloc]init];

t2 = t;

NSLog(@"%p",t2);

NSLog(@"1");

}

NSLog(@"%p",t2);

NSLog(@"2");

}

return 0;

}

======运行结果======

0x100300220

1

Test被释放

0x100300220

2

转载于:https://www.cnblogs.com/lqios/p/4288271.html

[objective-c] 08 - 内存管理相关推荐

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

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

  2. Objective -C Memory Management 内存管理 第一部分

    Objective -C Memory Management  内存管理  第一部分 Memory management is part of a more general problem in pr ...

  3. 1.1 objective-c中的内存管理

    备注:本来在一年前有一个出版社找到我,让我写一系列关于iOS性能优化的书.但是一直因为工作原因,没有能够按时交付.(其实就是自己懒)所以现在讲已经写好的部分章节分享到博客中,希望对大家有所帮助,如果有 ...

  4. 一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc

    大家好,我是无际. 有一周没水文了,俗话说夜路走多了难免遇到鬼. 最近就被一个热心网友喷了. 说我的文章没啥营养,所以今天来一篇烧脑的. 哈哈,开个玩笑,不要脸就没人能把我绑架. 主要是最近研发第二代 ...

  5. Objective-C 内存管理之ARC规则

    基本概念 ARC为自动引用计数,引用计数式内存管理的本质并没有改变,ARC只是自动处理"引用计数"的相关部分. 在编译上,可以设置ARC有效或无效.默认工程中ARC有效,若设置无效 ...

  6. python内存管理错误的是_关于Python内存管理,下列说法错误的是

    53 Python中变量可以不指定类型,会自动根据赋值语句来决定类型 同时,使用前必须赋值,在赋值的同时变量也就创建了 发表于 2018-02-28 22:50:02 回复(0) 61 本题答案选 B ...

  7. (0048)iOS开发之内存管理探究

    注意网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得 retainCount都是不准确的,特别是在对象拷贝操作之后你会发现 ...

  8. linux 内存管理 page fault带来的性能问题

    Linux进程如何访问内存 Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源. 原因后面会讲到. 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间,内存假 ...

  9. [内存管理] linux kernel内存映射实例分析

    作者:JHJ(jianghuijun211@gmail.com ) 日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm corte ...

最新文章

  1. html 鼠标单击单元格,vue-easytable点击表格中某个单元格操作
  2. 《系统集成项目管理工程师》必背100个知识点-76配置审计
  3. 嵌入式Linux系统运行流程图
  4. python实战excel_实战python 读写EXCEL表
  5. codeforces 938D Buy a Ticket 有初值的Dijkstra、有趣的题目
  6. 所有的图放到一个html,拖放是HTML5标准的组成部分,若想要把drag1图片放入d
  7. 微内核和宏内核的区别(短小、精悍)
  8. 飞秋的模拟实现代码,很好很山寨!
  9. 【音效处理】Vibrato 简介
  10. springmvc和json整合配置方法
  11. 如何卸载mysql5.6.28_如何完全删除MySQL以进行全新安装
  12. ValueError: Cannot feed value of shape (784,) for Tensor 'Placeholder:0', which has shape '(?, 784)'
  13. Python编程 whl文件安装库
  14. 软件体系结构风格---基于事件的隐式调用
  15. 游戏服务器背包设计与开发
  16. RabbitMQ消息队列(九):Publisher的消息确认机制
  17. 【源码共享】我花2小时写了微信官网的响应式布局HTML+CSS 换成旅行主题风格更炫酷了
  18. java软件测试基础
  19. div显示在上层_如何让div总是显示在最上层,而不致于被其他div遮挡
  20. 计算机主板diy,DIY电脑配件立即选——主板篇

热门文章

  1. 关于开源中国手机App的说明
  2. netnbsp;与nbsp;javascript脚本的几种交互方法
  3. 将试用版visual studio 2008升级为正式版 --更新
  4. UCloud首尔机房整体热迁移是这样炼成的
  5. shiro身份验证测试
  6. jsp页面验证码(完整实例)
  7. 了解 Windows Azure 存储的可伸缩性、可用性、持久性和计费
  8. C语言:为什么用fprintf(stderr,Error);比printf(Error);更好?
  9. HttpHandler:给指定路径下的图片添加水印显示
  10. java 歌词_请问吧内有大神用JAVA做过桌面歌词吗