[objective-c] 08 - 内存管理

 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/472349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机主板最常见的问题,计算机主板功能 电脑实用技巧解决常见问题

电脑实用技巧解决常见问题1.解决问题 插电即开机问题现象&#xff1a;有些朋友有关机后断开电源板电源的习惯&#xff0c;可是却常常被一个问题困扰&#xff0c;就是电源板一通电&#xff0c;计算机就自动开机了&#xff0c;Power键形同虚设。解决问题&#xff1a;有些主板在BI…

zeal刷新不出来_饥荒:游戏中的这些事物都是无中生有,几乎可以无限制刷新!...

在饥荒这款经典的生存类游戏中&#xff0c;其事物的“刷新”和许多其他的生存游戏不太一样&#xff0c;有些生存游戏的事物刷新是定时定量的&#xff0c;而饥荒则不完全&#xff0c;就拿牛群来形容&#xff0c;他们必须要有多个成年的牛进行繁殖才会出现新的牛&#xff0c;而像…

数字逻辑基础与verilog设计_数字电路学习笔记(五):逻辑设计基础

马上就要正式进入电路设计了&#xff0c;再来看最后一个知识点&#xff1a;逻辑设计吧。之前我们花了两章&#xff0c;探讨了逻辑运算是什么&#xff0c;怎么算&#xff1b;但还有最后一个大问题&#xff0c;巧妇难为无米之炊&#xff0c;我们得先有一个逻辑式&#xff0c;才能…

完整的开发一个ContentProvider步骤

1、定义自己的ContentProvider类&#xff0c;该类需要继承Android提供的ContentProvider基类。2、向Android系统注册这个"网站"&#xff0c;也就是在AndroidManifest.xml文件中注册这个ContentProvider&#xff0c;就像注册Activity一样。注册ContentProvider时需要为…

河北省高校计算机大赛,河北省教育厅关于举办2016年华北五省(市、自治区)及港澳台大学生计算机应用大赛河北赛区竞赛的通知...

有关高等学校&#xff1a;为深入贯彻落实《国家中长期教育改革和发展规划纲要(2010-2020年)》和教育部《关于全面提高高等教育质量的若干意见》(教高﹝2012﹞4号)精神&#xff0c;推动区域高等教育合作发展、资源共享&#xff0c;培养大学生创新能力、实践能力和团队协作意识&a…

adhoc包无法安装_iOS 5.1.1 设备不能安装AdHoc问题版本号

之前苹果更新了审计规范&#xff0c;要求必须支持64通过苹果的审核权限位架构的应用。但运营商表示反馈。使用iOS5.1.1该系统无法安装我们的包Adhoc版本号。认为非常莫名。由于我们在Deployment Target上确实写着5.1.1&#xff0c;全公司也都没人用这个系统了。在运营纠结这个问…

c盘清理代码_拒接卡顿,从c盘减负、系统修复及网络加速做起!奥利~~~

你是否有过系统卡顿&#xff0c;C盘饱满&#xff1f;你是否有过蓝屏、闪退&#xff1f;你是否有过网络爆卡&#xff1f;今天&#xff0c;他来了&#xff0c;一篇文章教会你清理C盘、修复系统、网络加速&#xff01;&#xff01;&#xff01;首先&#xff0c;按下winr键&#xf…

LeetCode 1833. 雪糕的最大数量(贪心)

文章目录1. 题目2. 解题1. 题目 夏日炎炎&#xff0c;小男孩 Tony 想买一些雪糕消消暑。 商店中新到 n 支雪糕&#xff0c;用长度为 n 的数组 costs 表示雪糕的定价&#xff0c;其中 costs[i] 表示第 i 支雪糕的现金价格。 Tony 一共有 coins 现金可以用于消费&#xff0c;他…

HDU 3605Escape(缩点+网络流之最大流)

题目地址&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid3605 本来打算昨天写两道题的&#xff0c;结果这个题卡住了&#xff0c;最后才发现是最后的推断条件出错了&#xff0c;推断满流的条件应该是与n的比較&#xff0c;居然写成与全部星球总容量的比較了。&#xf…

为学好计算机 要采取什么措施,计算机概念教学的内容及对策

摘 要:介绍有关概念教学的重要性及在概念教学中重视培养学生的思维能力、概念教学过程中应注意的问题和概念教学中采取的包括淡化、浅化、深化、跨越,在概念教学中应注意的问题,关键词:概念教学;淡化;浅化;深化;跨越中图分类号:G434文献标识码:AAccording to Different Content…

用递归计算一个数字每一位相加的结果。_leetcode 2 两数相加(c++)

两种解法&#xff0c;第一种是在原有的两个链表中选择更长的那个作为结果返回&#xff0c;虽然节约了空间&#xff0c;但是增加了时间复杂度&#xff0c;而且没有用到如何设置链表的增加与删除&#xff0c;第二种看起来清爽很多&#xff0c;逻辑也清晰。### 题目给出两个非空的…

LeetCode 1834. 单线程 CPU(排序 + 优先队列)

文章目录1. 题目2. 解题1. 题目 给你一个二维数组 tasks &#xff0c;用于表示 n​​​​​​ 项从 0 到 n - 1 编号的任务。 其中 tasks[i] [enqueueTimei, processingTimei] 意味着第 i​​​​​​​​​​ 项任务将会于 enqueueTimei 时进入任务队列&#xff0c;需要 pro…

tc溜溜865手机投屏卡_溜溜 TC Games 官网_专题

1、智能按键功能&#xff1a;该功能通过宏命令方式实现智能精准按键(vip)备注&#xff1a;a&#xff1a;在键位设置中勾选智能按键&#xff0c;并重新选用官方一键切雷vip键位b&#xff1a;目前仅支持软件内设置为 720 分辨率&#xff0c;及 *1080p 分辨率的手机c&#xff1a;该…

服务器支持磁盘阵列,服务器磁盘阵列、RAID级别的阐述

磁盘阵列磁盘阵列(Redundant Arrays of Independent Disks&#xff0c;RAID)&#xff0c;有“独立磁盘构成的具有冗余能力的阵列”之意。磁盘阵列是由很多价格较便宜的磁盘&#xff0c;组合成一个容量巨大的磁盘组&#xff0c;利用个别磁盘提供数据所产生加成效果提升整个磁盘系…

LeetCode 1835. 所有数对按位与结果的异或和(位运算 (ab)^(ac) = a(b^c) )

文章目录1. 题目2. 解题1. 题目 列表的 异或和&#xff08;XOR sum&#xff09;指对所有元素进行按位 XOR 运算的结果。 如果列表中仅有一个元素&#xff0c;那么其 异或和 就等于该元素。 例如&#xff0c;[1,2,3,4] 的 异或和 等于 1 XOR 2 XOR 3 XOR 4 4 &#xff0c;而 …

python入门基础系列_03python—9个基础常识-python小白入门系列

《python小白入门系列教程》 专栏 • 第03篇 文 | xc_718 深度好文&#xff1a;1828字 | 4分钟阅读 ​ 1. 注释 1&#xff09;单行注释&#xff1a;****# #注释内容 print(123) #123 print(abc) #abc print("abc") #abc **2&#xff09;多行注释&#xff1a; 或 **&q…

hihocoder1089 Floyd算法

题目链接&#xff1a;http://hihocoder.com/problemset/problem/1089 算法描述&#xff1a; floyd算法是求解图中任意两点最短路的经典算法&#xff0c;复杂度为O(n^3)。虽然我们完全可以用n次dijkstra算法来求任意两点的最短路&#xff0c;复杂度也是O(N^3)&#xff0c;但如果…

matlab 思维数组_matlab多维数组

1.一个三维数组由行、列和页三维组成&#xff0c;其中每一页包含一个由行和列构成的二维数组。2.利用标准数组函数创建多维数组Azeros(4,3,2)生成一个4行3列2页的三维全0数组&#xff0c;ones&#xff0c;rand和randn等函数有相似的用法。3.利用直接索引方式生成多维数组Azeros…

css英文左右对齐,中文英文左右padding一致两端对齐实现_js

先看下图&#xff1a;就是一个定宽的容器&#xff0c;左右padding值20像素&#xff0c;结果输入一段文字后(有中文也有英文字符)&#xff0c;会发现右侧根本就不对齐&#xff0c;有些地方距离右侧的空白大小也不是20像素&#xff0c;感觉不和谐&#xff0c;设计师就希望排列能够…

python判断字符类型编程_Python检测数据类型的方法总结

我们在用python进行程序开发的时候&#xff0c;很多时候我们需要检测一下当前的变量的数据类型。比如需要在使用字符串操作函数之前先检测一下当前变量是否是字符串。下面小编给大家分享一下在python中如何检测数据类型 首先我们打开CMD控制台&#xff0c;进入到python环境&…