黑马程序员——OC学习——内存管理

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

1. 内存管理的基本概念及范围

由于移动设备的内存极其有限,所以每个APP占用的内存也是有限的,当APP占用内存过多时,系统就会发出内存警告。为了保证整个系统运行流畅,需要回收一些不需要再继续使用的内存空间,否则系统会崩溃。

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

内存分区分为以下5个区:

栈区——存放局部变量

堆区——程序运行过程中动态分配的存储空间

BSS段——没有初始化的全局变量和静态变量

数据区——已经初始化的全局变量、静态变量和字符串常量

代码段——程序执行代码的一块内存区域

堆区内存不连续,无法自动释放,内存管理只管理OC对象

通过操作对象的“引用计数器”对内存进行管理


2. 内存管理的原理及分类

内存管理的原理:对象所有权及引用计数

Cocoa所有权概念:任何自己创建的对象都归自己所有

引用计数器(retainCount):标志被引用的次数,存储当前对象有几个使用者,是判断对象是否回收的依据

例外:对象值为nil时,引用计数器为0,但不回收空间,因为没有分配空间

对引用计数器的操作:

retain消息:使计数器+1,该方法返回对象本身

release消息:使计数器-1

retainCount消息:获得当前对象的retainCount的值

对象被销毁:

当retainCount为0时,对象被销毁,内存被回收。对象被销毁时,系统自动向对象发送一条dealloc消息,一般会重写dealloc消息,重写dealloc消息必须在代码块最后调用[super dealloc]。

任何一个对象,在刚创建时,引用计数器都为1.

OC内存管理分为3类:

  • Manual Reference Counting (MRC)
  • Automatic Reference Counting(ARC)
  • garbage collection(垃圾回收)IOS不支持垃圾回收
3. 手动管理内存

当我们创建一个项目时,默认是ARC的(自动内存管理),手动把ARC项目改成MRC项目的方法之一如下:

a.选中项目,此时Xcode右侧会出现如图设置信息

黑马程序员——OC学习——内存管理_第1张图片


b. 在同时选中Build Settings和Levels时在右侧搜索框搜索auto,此时会出现ARC设置

黑马程序员——OC学习——内存管理_第2张图片


c.把ARC设置中目标target下的Yes设置为No,其他相应的Yes也变为No,此时就是MRC管理



4. 内存管理的原则

  • 只要某个对象还有使用者,该对象就不会被回收
  • 如果想使用某个对象,就让该对象的引用计数器+1
  • 不再使用某个对象时,应该让该对象的引用计数器-1
  • 谁创建,谁release
  • 谁retain,谁release
野指针:定义的之后怎变量没有初始化或者指针变量指向的空间已经被释放了

内存泄露:Person *p=[Person new]; 如果栈区的p已经释放了,而p指向的堆区空间还没有释放,堆区的空间就泄露了。

5. MRC下单个对象内存管理

为了避免使用僵尸对象,在对象release后,将对象赋值为nil,示例代码如下:

#import 
#import "Dog.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Dog *d=[Dog new];
        //调用eat方法
        [d eat];
        //release对象
        [d release];
        //给对象赋值nil,避免野指针操作
        d=nil;
    }
    return 0;
}

6. MRC下多个对象内存管理

当B对象作为A对象的成员变量时,A、B对象之间存在关联关系。A对象set方法的书写要判断是否是同一对象,是同一对象是set方法不起作用,不是同一对象时要先release掉旧值,再retain新值。

比如有两个类Person、Dog,Dog类是Person类的一个实例变量,示例代码如下:

-(void)setDog:(Dog *)dog{
    //判断原对象和传进来的对象是不是同一对象
    if (_dog!=dog) {
        //release就对象
        [_dog release];
        //对新对象做一次retain
        _dog=[dog retain];
    }
}

基本数据类型的数据作为实例变量set方法直接赋值,eg:int _speed的set方法如下:

-(void)setSpeed:(int)speed{
    //基本数据类型直接赋值
    _speed=speed;
}

7. @property参数

格式: @property (参数1,参数2)数据类型 方法名

@property的参数有3类7个,原子性(atomic,nonatomic),读写属性(readwrite,readonly),set方法处理(assign,retain,copy)

参数 说明
atomic 对属性加锁,多线程下安全,是默认值
nonatomic 对属性不加锁,多线程下不安全,但是速度快
readwrite 生成getter,setter,默认值
readonly 只生成getter方法
assign 直接赋值,默认值
reain 先release原来的值,再retain新值
copy 先release原来的值,再copy新值

assign适用于基本数据类型,retain用于OC对象类型,在MRC内存管理方式下,对象类型作为实例变量时要重写dealloc方法,释放一次对象。

用@property替换set和get方法的名称

eg:@property (nonatomic,assign,setter=isVip,getter=isVip)BOOL vip;

8. autorelease的使用

autorelease是一个栈结构的自动释放池。是一种支持引用计数器的内存管理方式,可以暂时保存某个对象在自动释放池结束的时候对其中的每个对象发送一次release消息。

autorelease的原理:延迟了release的调用,把对象加入到自动释放池中不会改变对象的引用计数

自动释放池的创建,在IOS5.0以后,用以下方式创建:

@autoreleasepool

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


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

把对象加入到自动释放池,在自动释放池中调用对象的autorelease方法:[对象 autolease],示例代码如下:

 @autoreleasepool {
        Person *p=[Person new];
        //把对象加入到自动释放池中
        [p autorelease];
    } //自动释放池结束时向p对象发送一次release消息

autorelease经常用来在类方法中快速创建一个对象,示例代码如下:

#import 
@interface Person:NSObject
+(instancetype)person;
@end

@implementation Person
//重写对象的dealloc方法
- (void)dealloc
{
    NSLog(@"人对象已经销毁");
    [super dealloc];
}
//快速创建一个对象
+(instancetype)person{
    return [[[self alloc] init] autorelease];
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p=[Person person];
        }//自动释放池结束后p对象被销毁
    return 0;
}

9. ARC下内存管理

ARC是编译器特性,编译时会在代码合适的位置插入retain,release,autorelease。

ARC的判断准则是只要没有强指针指向,对象就会被销毁。

指针默认都是强指针,关键字是__strong,弱指针关键字是__weak.

使用ARC方式管理内存,正常创建对象,不用释放。

在ARC方式下@property参数不使用retain,而使用strong或weak。

ARC机制下几个明显的标志

a. 不允许使用对象的release方法

b. 不允许使用autorelease方法

c. 重写dealloc时不能调用[super dealloc].


10. 内存管理下循环引用问题

MRC下对象循环引用的解决方案是@property的参数一端使用retain,一端用assign

ARC下对象循环引用的解决方案是@property的参数一端使用strong,一端用weak







你可能感兴趣的:(黑马程序员——OC学习——内存管理)