Objective-C 内存管理

结论:谁alloc,copy,retain,谁release

管理范围:任何继承NSObject的对象,基本数据类型不用进行管理

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

出现原因:

1.内存溢出:没有指向对象的指针,但是对象没有被回收

2.野指针异常:指向僵尸对象的指针,僵尸对象是内存被回收的对象。

给空指针发送消息不会报错

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

内存管理方式:

1.手工引用计数(manual reference counting)

2.自动引用计数(auto reference counting)

MRC的内存管理机制:引用计数

ARC是基于MRC的,ARC是编译器特性,而不是运行时特性

内存管理原则

(一)原则

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

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

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

(二)谁创建,谁release

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

(2)不是你创建的就不用你去负责

第二条意味着通过便利构造器和访问器间接得到对象,实际上没有获得对象的所有权,不需要负责对象的释放,只有通过alloc,retain,copy等手段得到的对象,才拥有对象的所有权,所以不要对通过便利构造器和访问器得到的对象进行release

(三)谁retain,谁release

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

(四)总结

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

属性赋值的内存管理:

retain:应该先对旧的对象执行release,然后对新的对象retain(适用oc对象类型)

assign:直接赋值

copy:对旧的对象执行release,对新的对象copy

retain下的属性内部实现

- (void) setName:(NSString *)name {

if(_name != name) {

[_name release];

_name = [name retain];

}

}

- (NSString *) name {

return [[_name retain] autorelease];

}

使用retain让对象的引用计数加1,如果单纯赋值没有使用retain,对象的引用计数不会变化

assign、retain、copy对应不同的setter实现。为实例变量赋值时,尽量使用setter方法,再次赋值时,会把之前值release。

dealloc在对象引用计数为0时自动调用,不要显式调用。

dealloc实现内部,先要释放实例变量,然后执行[super dealloc]。

便利构造器的内存管理是借助autorelease实现的。

集合会管理自己的元素。 KVC是一种间接访问实例变量的方法。 ARC系统管理内存,不需要开发人员手动管理。

当对象autorelease时,会被添加到autoreleasepool中,当autoreleasepool被销毁时,对象被销毁

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

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

nonatomic设置属性setter,getter操作不是原子性的,不是线程安全的,atomic是原子性的

OC中copy的作用是:利用一个源对象产生一个副本对象(copy必须遵循NSCopying协议)

特点:1、修改源对象的属性和行为,不会影响副本对象。

2、修改副本对象的属性和行为,不会影响源对象。

使用方式:

一个对象可以调用copy或mutableCopy方法来创建一个副本对象。

1、copy:创建的时不可变副本(如NSString、NSArray、NSDictionary)。

2、mutableCopy:创建的可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)。

使用copy功能的前提:

1、copy:需要遵守NSCopying协议,实现copyWithZone:方法。

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end

2、mutableCopy : 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(NSZone *)zone;

@end

只有源对象和副本对象都不可变时,才是浅复制,其他都是深复制。

深复制和浅复制的区别:

1.深复制(深拷贝、内容拷贝、deep copy):

特点:1、源对象和副本对象是不同的两个对象;

2、源对象引用计数器不变,副本对象计数器为1(因为是新产生的)。

本质:产生了新对象。

2.浅复制(浅拷贝、指针拷贝、shallow copy):

特点:1、源对象和副本对象是同一对象;

2、源对象(副本对象)引用计数器+1,相当于做一次retain操作。

本质:没有产生新对象。

浅拷贝:对象开辟新的空间,对象的内存地址不同,实例变量指向同一块内存

- (id) copyWithZone:(NSZone*)zone

{

Person*p = [[Person allocWithZone:zone] init];

p.string=self.string;

return p;

}

深拷贝:对象和实例变量的内存地址都不同

- (id) copyWithZone:(NSZone*)zone

{

Person*p1 = [[Person allocWithZone:zone] init];

p1.string= [self mutableCopy];

return p1;

}

伪拷贝:

- (id) copyWithZone:(NSZone*)zone

{

return[self retain];

}

附、ARC的特点总结:

(1)不允许调用release,retain,retainCount

(2)允许重写dealloc,但是不允许调用[super dealloc]

(3)@property的参数:

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

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

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

补充

让程序兼容ARC和非ARC部分。转变为非ARC  -fno-objc-arc  转变为ARC的, -f-objc-arc 。

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

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

永远不要在初始化方法使用self形式的代码调用本类的方法,因为此时本类的方法可能未初始化完成,调用可能会发生crash,但是父类的方法可以调用,因为父类已经初始化完成,调用不会发生问题。


转载请注明:作者SmithJackyson

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 之前的两篇拙文C语言-内存管理基础、C语言-内存管理深入 介绍了关于C语言在内存管理方面的相关知识。但是对于...
    老板娘来盘一血阅读 3,773评论 25 36
  • 1.1 什么是自动引用计数#### 顾名思义,自动引用计数(ARC,Automatic Reference Cou...
    见哥哥长高了阅读 603评论 0 1
  • 内存管理的问题#   先看看下面的几段代码,重温一下使用内存常见的问题。   C语言中内存操作常见错误: 内存分配...
    DeanYan阅读 1,030评论 0 1
  • 内存管理是程序在运行时分配内存、使用内存,并在程序完成时释放内存的过程。在Objective-C中,也被看作是在众...
    蹲瓜阅读 3,160评论 1 8
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,989评论 1 16