Objective-c:内存管理

基本原理

简介

  • 内存管理是程序设计中常见的资源管理的一部分。每个计算机系统可供程序使用的资源都是有限的,这些资源包括内存、打开文件、数量及网络连接等等。如果你使用了某种资源,例如因为打开文件而占用了资源,那么你需要随后对其进行清理。如果我们只分配而不释放内存,则将发生内存泄露:程序的内存占用不断增加,最终会耗尽并导致程序奔溃。

  • 管理范围:任何继承NSObject的对象,对其他的基本数据类型无效。

  • 什么是内存?

    答:程序运行中临时分配的存储空间,在程序结束后释放;本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。

  • 什么是内存管理?

    答:内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。

  • 我们为什么要进行内存管理?

    程序运行时,内存资源是有限的,Objective-C编写程序过程中创建实例化对象会占用内存,程序使用的内存会随着程序中对象的增加而不断增加,久而久之,最终系统资源被消耗殆尽。Mac OS 有垃圾回收机制,但iOS没有,iOS内存远不足于Mac OS系统,iOS将内存管理的任务交给了开发者。

程序内存分配

  • 堆区:有程序员分配释放,若程序员不释放,可能会造成程序泄露。注意,它与数据结构中的堆是两回事,分配方式倒是类似于链表;

  • 栈区:由编译器自动分配释放,存放函数的参数值和局部变量的值;

  • 全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

  • 文字常量区:常量字符串就是放在这里的,程序结束后由系统释放。

  • 程序代码区:存放函数体的二进制代码。

对象的基本结构

  • 每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。

  • 在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器。

引用计数概念

  • 判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。

引用计数操作

给对象发送消息,进行相应的计数器操作。下图为对象操作与Objective-c方法的对应:

对象操作Objective-c方法
生成并持有对象alloc/new/copy/mutableCopy等方法
释放对象retain方法
释放对象release方法
废弃对象dealloc方法

相关概念和使用注意

  • 野指针错误:访问了一块坏的内存(已经被回收的,不可用的内存)。

  • 僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)

    这里写图片描述

  • 空指针:没有指向任何东西的指针(存储的东西是:0、NULL、nil),给空指针发送消息不会报错。

Tips

1、不能使用[p retaion]让僵尸对象起死复生。

内存管理原则

  • 原则

    1、 只要还有人在使用某个对象,那么这个对象就不会被回收;

    2、只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;

    3、当你不想使用这个对象时,应该让对象的引用计数器-1;

  • 谁创建,谁release

    1、如果你通过alloc、new、copy以及mutableCopy来创建了一个对象,那么你就必须调用release或者autorelease方法;

    2、不是你创建的就不用你去负责;

  • 谁retain,谁release

    1、只要你调用了retain,无论这个对象时如何生成的,你都要调用release

总结

  • 有始有终,有加就应该有减。曾经让某个对象计数器+1,就应该让其在最后-1;

手动管理内存设置方法

  • 使用Xcode4.2或以上版本,系统默认采用ARC管理内存,即:自动引用技术(ARC,Automatic Reference Counting),ARC是指内存管理中对引用采取自动计数的技术。ARC机制下,引用计数操作如retain、release等将无法使用,需切换到手动管理内存。

  • 要从自动引用计数回归到手动引用技术,实现步骤如下:

    选择工程目录,进入TARGETS >点击Build Settings > 在搜索框输入 automatic 搜索 >选择Objective-C Automatic Reference Counting 将其列表值置为NO即可。如下图

    这里写图片描述

内存管理代码规范

set方法的代码规范

  • 基本数据类型:直接赋值
- (void)setAge:(NSInteger)age {
    _age = age;
}
  • OC对象类型
- (void)setName:(NSString *)name {
    // 判断是否为新传入的对象
    if (name != _name) {
        // 对旧对象做一次release释放
        [_name release];
        // 对新对象做一次retain持有
        _name = [name retain];
    }
}

dealloc方法的代码规范

  • 对self(当前)所拥有的的其他对象做一次release操作
- (void)dealloc {

    [_name release];

    [super dealloc];
}

遍历构造方法的代码规范

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {

    Person *person = [[Person alloc] initWithName:name age:age];

    return  [person autorelease];
}

Tips

1、一定要[super dealloc];,而且要放到最后

@property属性参数

  • 内存管理相关参数

    1、Retain:对对象release旧值,retain新值(适用于OC对象类型)

    2、Assign:直接赋值(默认,适用于非oc对象类型)

    3、Copy:release旧值,copy新值

  • 是否要生成set方法(若为只读属性,则不生成)

    1、Readonly:只读,只会生成getter的声明和实现

    2、Readwrite:默认的,同时生成setter和getter的声明和实现

  • 多线程管理

    1、Nonatomic:高性能,一般使用这个

    2、Atomic:低性能

  • Set和Get方法的名称

    修改set和get方法的名称,主要用于布尔类型。因为返回布尔类型的方法名一般以is开头,修改名称一般用在布尔类型中的getter。

@propery(setter = setLogin, getter = isLogin) BOOL login;

 BOOL b = person.isLogin; // 调用

内存管理中的循环引用问题以及解决

案例:每个老师都有学生,每个学生都有老师,不能使用#import的方式相互包含,这就形成了循环引用。

解决办法:使用@class 类名,解决循环引用问题,提高性能。

@class仅仅告诉编译器,在进行编译的时候把后面的名字作为一个类来处理。

  • @class仅仅告诉编译器,在进行编译的时候把后面的名字作为一个类来处理。

  • 开发中引用一个类的规范

    • 在.h文件中使用@class来声明类

    • 在.m文件中真正要使用到的时候,使用#import来包含类中的所有东西

  • 两端循环引用的解决方法:一端使用retain,一端使用assign(使用assign的在dealloc中也不用再release)

Autorelease

基本用法

  • 会将对象放到一个自动释放池中

  • 当自动释放池被销毁时,会对池子里的所有对象做一次release

  • 会返回对象本身

  • 调用完autorelease方法后,对象的计数器不受影响(销毁时影响)

好处

  • 不需要再关心对象释放的时间

  • 不需要再关心什么时候调用release

使用注意

  • 占用内存较大的对象,不要随便使用autorelease,应该使用release来精确控制

  • 占用内存较小的对象使用autorelease,没有太大的影响

错误写法

  • 连续调用多次autorelease,释放池销毁时执行两次release。

  • Alloc之后调用了autorelease,之后又调用了release。

自动释放池

  • 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。

  • 当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

自动释放池的创建方式

  • iOS 5.0 以前的创建方式
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];

// .....

[pool  release]; // 用于 OX S环境 
  • iOS 5.0 以后
@autoreleasepool

{//开始代表创建自动释放池

·······

}//结束代表销毁自动释放池

Autorelease注意

  • 系统自带的方法中,如果不包含alloc new copy等,则这些方法返回的对象都是autorelease的,如[NSDate date];

  • 开发中经常会写一些类方法来快速创建一个autorelease对象,创建对象时不要直接使用类名,而是使用self。

ARC内存管理机制

ARC判断标准

  • 只要没有强指针指向对象,对象就会被释放。

指针分类:

  • 强指针:默认的情况下,所有的指针都是强指针,关键字strong

  • 弱指针:__weak关键字修饰的指针

1、声明一个弱指针如下:

__weak Person *p;

2、ARC中,只要弱指针指向的对象不在了,就直接把弱指针做清空操作。

__weak Person *p = [[Person alloc] init]; // 不合理,对象一创建出来就被释放掉,对象释放掉后,ARC把指针自动清零。

3、ARC中在property处不再使用retain,而是使用strong,在dealloc中不需要再[super dealloc]。

@propertynonatomicstrong)Person *person; // 意味着生成的成员变量_person是一个强指针,相当于以前的retain。

4、如果换成是弱指针,则换成weak,不需要加__

ARC的特点总结

  • 不允许调用release,retain,retainCount

  • 不允许重写dealloc,但是不允许调用[super dealloc]

  • @property的参数:

    1、Strong:相当于原来的retain(适用于OC对象类型),成员 变量是强指针

    2、Weak:相当于原来的assign(适用于oc对象类型),成员变量是弱指针

    3、Assign:适用于非OC对象类型(基础类型)

补充

尽管目前系统提倡使用ARC技术,但是其自身也有一定的缺陷,大部分开发者还是使用非ARC管理内存,为了让程序兼容ARC和非ARC部分,需进行如下操作:

  • 转变为非ARC: -fno-objc-arc

  • 转变为ARC: -f-objc-arc

Tips

1、ARC也需要考虑循环引用问题:一端使用retain,另一端使用assign。

2、提示:字符串是特殊的对象,但不需要使用release手动释放,这种字符串对象默认就是autorelease的,不用额外的去管内存。

这里写图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值