Kevin的IOS开发专题

OC内存管理详解

2017-06-05  本文已影响142人  伶俐ll

ARC

一、简介

在Objective-C中采用Automatic Reference Counting (ARC)机制,是编译器特性,让编译器来进行内存管理。ARC是LLVM编译器和Runtime系统相互协作的一个结果,ARC利用LLVM编译器自动帮我们生成releaseretainautorelease代码,像弱引用这样又是利用Runtime在程序运行过程中监控到对象销毁的时候将指向对象的弱引用清空。

在Xcode4.2或以上版本,只要将编译器选项中设置ARC为有效状态(操作如下图),编译器将自动在代码合适的地方插入retainreleaseautorelease代码,就无需再次手动输入,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。

Snip20170604_4.png

编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象(判断原则:只要还有一个强指针变量指向对象,对象就会保持在内存中;只要没有强指针变量指向对象,就默认该对象不再需要被使用,该对象就会被释放)。

如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。

二、ARC下的所有权修饰符

__strong
  id obj = [[NSObject alloc]init];
  id __strong obj = [[NSObject alloc]init];
  id  __strong obj0;
  id  __strong obj0 = nil;
__weak
__unsafe_unretained

MRC

在引入ARC之前((2012年发布)Xcode4.2之前版本),OC的内存管理需要由开发人员手动维护,需要程序员自己编写releaseretain等代码。

一、引用计数的存储

OC中内存的管理是依赖对象的引用计数器(占用四个字节)来进行的.OC中每个对象都有一个与之对应的整数,叫“引用计数器”。

二、MRC下的引用计数式内存管理
三、对象是否被销毁的判断

当一个对象的引用计数器值为0时,这个对象占用的内存就会被系统回收,对象即将被销毁时系统会自动给对象发送一条dealloc消息废弃对象。因此,根据deallloc方法有没有被调用就可以判断出对象是否被销毁。

四、防止野指针

如果一个对象被释放后,那么最后引用它的变量需要手动设置为nil(空指针:给空指针发送消息不会报错),否则可能造成野指针错误。(只要一个对象对释放了,我们就称这个对象为“僵尸对象”,当一个指针指向一个僵尸对象,我们就称这个指针为野指针,如果给一个野指针发送消息会报错)

五、MRC内存管理原则

对象之间可能交叉引用,此时需要遵循一个法则:谁创建,谁释放

六、autorelease
autoreleasepool的本质

将如下代码转成c++代码:

  @autoreleasepool {
        LZPerson *person = [[[LZPerson alloc] init] autorelease];
    }
 struct __AtAutoreleasePool {
    __AtAutoreleasePool() { // 构造函数,在创建结构体的时候调用
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
 
    ~__AtAutoreleasePool() { // 析构函数,在结构体销毁的时候调用
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
 
    void * atautoreleasepoolobj;
 };
 
 {
    __AtAutoreleasePool __autoreleasepool;
    LZPerson *person = ((LZPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LZPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LZPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LZPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
 }

由此可知,autoreleasepool本质上是一个__AtAutoreleasePool类型的结构体,在结构体内部定义了一个构造函数和析构函数,还有一个void*类型的指针atautoreleasepoolobj,在使用autoreleasepool的时候会在大括号开始的时候声明一个局部变量__autoreleasepool,在定义局部变量的时候会调用结构体内部的构造函数objc_autoreleasePoolPush(),在大括号结束的时候会调用析构函数objc_autoreleasePoolPop()。因此上述代码的本质可以说是如下:

 atautoreleasepoolobj = objc_autoreleasePoolPush();
 LZPerson *person = [[[LZPerson alloc] init] autorelease];
 objc_autoreleasePoolPop(atautoreleasepoolobj);

通过查看runtime源码可知:objc_autoreleasePoolPush()函数会调用AutoreleasePage类的push方法,objc_autoreleasePoolPop()函数会调用AutoreleasePage类的pop方法。因此得出结论:自动释放池的主要底层数据结构是:__AtAutoreleasePoolAutoreleasePoolPage;
调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的。

AutoreleasePoolPage的结构

每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址,所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起。其中child成员变量指向的是下一个AutoreleasePoolPage对象,parent成员变量指向的是上一个AutoreleasePoolPage对象,第一个AutoreleasePoolPage对象的parent是空的,最后一个AutoreleasePoolPage对象的child是空的。

屏幕快照 2018-08-01 上午9.37.18.png
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *p1 = [[[MJPerson alloc] init] autorelease];
        MJPerson *p2 = [[[MJPerson alloc] init] autorelease];
        _objc_autoreleasePoolPrint();
    }
    return 0;
}

打印结果:
/*
objc[65454]: ##############
objc[65454]: AUTORELEASE POOLS for thread 0x100390380
objc[65454]: 3 releases pending.  
objc[65454]: [0x100804000]  ................  PAGE  (hot) (cold)
objc[65454]: [0x100804038]  ################  POOL 0x100804038
objc[65454]: [0x100804040]       0x10055c000  MJPerson
objc[65454]: [0x100804048]       0x1005573f0  MJPerson
objc[65454]: ##############

*/
autorelease对象会在什么时机被调用release
方法里有局部对象, 出了方法后会立即释放吗

自动释放池有如下两种写法:

//1、 利用NSAutoreleasePool类
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
id obj = [[NSObject alloc]init];
[obj autorelease];
[pool drain];//在执行这行代码时,会给obj发送一条release消息
// 2、@autoreleasepool关键字声明一个代码块
@autoreleasepool{
     id obj = [[NSObject alloc]init];
     [obj autorelease];
}
autorelease使用注意点:
  -(id)object{
      id obj = [[NSObject alloc]init];
      [obj autorelease];
      return obj;
}

property修饰符

一、 访问权限修饰符:readwrite | readonly
二、 指定getter和setter方法名称修饰符:

getter=setter=这两个属性修饰符用于设置自定义生成的getter和setter方法名,使用之后将不再使用系统默认的setter和getter方法名。使用代码演示如下:

@property(getter = getMethodName, setter = setMethodName) Object *obj;
三、内存管理修饰符
@property(retain) Object *obj;
//setter方法
-(void)setObj:(Object *)obj{
       if(_obj != obj){
        [_obj release];
        _obj = [obj retain];
     }
}
四、原子性修饰符:atomic | nonatomic
上一篇下一篇

猜你喜欢

热点阅读