博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OC内存管理--对象的生成与销毁
阅读量:6932 次
发布时间:2019-06-27

本文共 9223 字,大约阅读时间需要 30 分钟。

原文链接

在iOS开发中了,我们每天都会使用+ alloc- init这两个方进行对象的初始化。我们也这知道整个对象的初始化过程其实就是开辟一块内存空间,并且初始化isa_t结构体的过程

alloc的实现

+ (id)alloc {    return _objc_rootAlloc(self);}id _objc_rootAlloc(Class cls) {    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);}复制代码

整个过程其实就是NSObjectcallAlloc方法的实现。

callAlloc

/* cls:CustomClass checkNil:是否检查Cls allocWithZone:是否分配到指定空间,默认为false,内部会对其进行优化*/static ALWAYS_INLINE idcallAlloc(Class cls, bool checkNil, bool allocWithZone=false) {    //没有class或则checkNil为YES,返回空    if (slowpath(checkNil && !cls)) return nil;//确保只有Objective-C 2.0语言的文件所引用#if __OBJC2__    //判断class有没有默认的allocWithZone方法    if (fastpath(!cls->ISA()->hasCustomAWZ())) {        // class可以快速分配        if (fastpath(cls->canAllocFast())) {            //hasCxxDtor();是C++析构函数,判断是否有析构函数            bool dtor = cls->hasCxxDtor();            //申请class的内存空间            id obj = (id)calloc(1, cls->bits.fastInstanceSize());            if (slowpath(!obj)) return callBadAllocHandler(cls);            //初始化isa指针            obj->initInstanceIsa(cls, dtor);            return obj;        }        else {            //使用class_createInstance创建class            id obj = class_createInstance(cls, 0);            if (slowpath(!obj)) return callBadAllocHandler(cls);            return obj;        }    }#endif        //说明有默认的allocWithZone的方法,调用allocWithZone方法    if (allocWithZone) return [cls allocWithZone:nil];    return [cls alloc];}复制代码

__OBJC2__下当前类有没有默认的allocWithZone方法是通过hasCustomAWZ()函数判断的。YES代表有则会调用[cls allocWithZone:nil]方法。NO代表没有,这时候会根据当前类是否可以快速分配,NO的话调用class_createInstance函数;YES则分配内存并初始化isa。

allocWithZone

+ (id)allocWithZone:(struct _NSZone *)zone {    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);}id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) {    id obj;#if __OBJC2__    // allocWithZone under __OBJC2__ ignores the zone parameter    (void)zone;    obj = class_createInstance(cls, 0);#else    if (!zone) {        obj = class_createInstance(cls, 0);    }    else {        obj = class_createInstanceFromZone(cls, 0, zone);    }#endif    if (slowpath(!obj)) obj = callBadAllocHandler(cls);    return obj;}复制代码

allocWithZone函数的本质是调用_objc_rootAllocWithZone函数。

_objc_rootAllocWithZone的逻辑分为两种情况:

  1. 先判断是否是__OBJC2__,如果是则调用class_createInstance
  2. 判断zone是否为空,如果为空调用class_createInstance,如果不为空,调用class_createInstanceFromZone
//class_createInstanceid class_createInstance(Class cls, size_t extraBytes) {    return _class_createInstanceFromZone(cls, extraBytes, nil);}//class_createInstanceFromZoneid class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) {    return _class_createInstanceFromZone(cls, extraBytes, zone);}复制代码

class_createInstanceclass_createInstanceFromZone的本质都是调用_class_createInstanceFromZone

另外通过前面的源代码我们可以发现:用alloc方式创建,只要当前类有allocWithZone方法,最终一定是调用class_createInstance

_class_createInstanceFromZone

static __attribute__((always_inline)) id_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,                               bool cxxConstruct = true,                               size_t *outAllocatedSize = nil) {    if (!cls) return nil;    assert(cls->isRealized());    bool hasCxxCtor = cls->hasCxxCtor();//构造函数    bool hasCxxDtor = cls->hasCxxDtor();//析构函数    bool fast = cls->canAllocNonpointer(); //是对isa的类型的区分,如果一个类不能使用isa_t类型的isa的话,fast就为false,但是在Objective-C 2.0中,大部分类都是支持的    //在分配内存之前,需要知道对象在内存中的大小,也就是instanceSize的作用。对象必须大于等于16字节。    size_t size = cls->instanceSize(extraBytes);    if (outAllocatedSize) *outAllocatedSize = size;    id obj;    if (!zone  &&  fast) {        //分配内存空间        obj = (id)calloc(1, size);        if (!obj) return nil;        //初始化isa指针        obj->initInstanceIsa(cls, hasCxxDtor);    } else {        //此时的fast 为 false        //在C语言中,malloc表示在内存的动态存储区中分配一块长度为“size”字节的连续区域,返回该区域的首地址;calloc表示在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址。        if (zone) {            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);        } else {            obj = (id)calloc(1, size);        }        if (!obj) return nil;        //初始化isa指针        obj->initIsa(cls);    }    if (cxxConstruct && hasCxxCtor) {        obj = _objc_constructOrFree(obj, cls);    }    return obj;}复制代码

初始化isa

_class_createInstanceFromZone中不光开辟了内存空间,还初始化了isa。初始化isa的方法有initInstanceIsainitIsa,但是本质都是调用initIsa(Class cls, bool nonpointer, bool hasCxxDtor)

inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) {     assert(!isTaggedPointer());         if (!nonpointer) {        isa.cls = cls; //obj->initIsa(cls)    } else {        //obj->initInstanceIsa(cls, hasCxxDtor);        assert(!DisableNonpointerIsa);        assert(!cls->instancesRequireRawIsa());        isa_t newisa(0);#if SUPPORT_INDEXED_ISA        assert(cls->classArrayIndex() > 0);        newisa.bits = ISA_INDEX_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.indexcls = (uintptr_t)cls->classArrayIndex();#else        newisa.bits = ISA_MAGIC_VALUE;        // isa.magic is part of ISA_MAGIC_VALUE        // isa.nonpointer is part of ISA_MAGIC_VALUE        newisa.has_cxx_dtor = hasCxxDtor;        newisa.shiftcls = (uintptr_t)cls >> 3;#endif        // This write must be performed in a single store in some cases        // (for example when realizing a class because other threads        // may simultaneously try to use the class).        // fixme use atomics here to guarantee single-store and to        // guarantee memory order w.r.t. the class index table        // ...but not too atomic because we don't want to hurt instantiation        isa = newisa;    }}复制代码

根据《OC引用计数器的原理》,现在再看一下初始化isa的方法。这个方法的意思是首先判断是否开启指针优化。

没有开启指针优化的话访问 objc_objectisa会直接返回isa_t结构中的cls变量,cls变量会指向对象所属的类的结构。

开启指针优化的话通过newisa(0)函数初始化一个isa,并根据SUPPORT_INDEXED_ISA分别设置对应的值。iOS设备的话这个值是0,所以执行else的代码。

到这里alloc的实现过程已经结束了,根据上面的源码分析,用一张图表示上述过程:

这里可能会有个疑问,既然alloc将分配内存空间和初始化isa的事情都做了,那么init的作用是什么呢?

init

- (id)init {    return _objc_rootInit(self);}id _objc_rootInit(id obj) {    return obj;}复制代码

init的作用就是返回当前对象。这里有个问题既然init只是返回当前对象,为什么要多此一举呢?

Apple给出的注释:

In practice, it will be hard to rely on this function. Many classes do not properly chain -init calls.

意思是在实践中,很难依靠这个功能。许多类没有正确链接init调用。所以这个函数很可能不被调用。也许是历史遗留问题吧。

new

+ (id)new {    return [callAlloc(self, false/*checkNil*/) init];}复制代码

所以说UIView *view = [UIView new];UIView *view = [[UIView alloc]init];是一样的。

dealloc

分析了对象的生成,我们现在看一下对象是如何被销毁的。dealloc的实现如下:

- (void)dealloc {    _objc_rootDealloc(self);}void _objc_rootDealloc(id obj) {    assert(obj);    obj->rootDealloc();}inline voidobjc_object::rootDealloc() {    if (isTaggedPointer()) return;  // fixme necessary?    if (fastpath(isa.nonpointer  &&                   !isa.weakly_referenced  &&                   !isa.has_assoc  &&                   !isa.has_cxx_dtor  &&                   !isa.has_sidetable_rc))    {        assert(!sidetable_present());        free(this);    }     else {        object_dispose((id)this);    }}复制代码

rootDealloc分为三种情况:

  1. 如果是TaggedPointer,直接return;
  2. 进行一些关于isa的条件判断,如果满足就释放分配的内存控件;
  3. 调用object_dispose函数,这是最重要的;

objc_destructInstance

我们先看object_dispose函数的源码:

id object_dispose(id obj) {    if (!obj) return nil;    objc_destructInstance(obj);        free(obj);    return nil;}复制代码

做了两件事情:

  1. 调用objc_destructInstance函数
  2. 释放分配的内存空间

objc_destructInstance的实现如下:

/************************************************************************ objc_destructInstance* Destroys an instance without freeing memory. * Calls C++ destructors.* Calls ARC ivar cleanup.* Removes associative references.* Returns `obj`. Does nothing if `obj` is nil.**********************************************************************/void *objc_destructInstance(id obj) {    if (obj) {        // Read all of the flags at once for performance.        bool cxx = obj->hasCxxDtor();//是否有析构函数        bool assoc = obj->hasAssociatedObjects();//是否有关联对象        // This order is important.        if (cxx) object_cxxDestruct(obj);//调用析构函数        if (assoc) _object_remove_assocations(obj);//删除关联对象        obj->clearDeallocating();//清空引用计数表并清除弱引用表    }    return obj;}复制代码

objc_destructInstance做了三件事情:

  1. 执行object_cxxDestruct调用析构函数
  2. 执行_object_remove_assocations删除关联对象
  3. 执行clearDeallocating清空引用计数表并清除弱引用表,将所有weak引用指nil(这也解释了为什么使用weak能自动置空)

object_cxxDestruct

在源码中object_cxxDestruct的实现由object_cxxDestructFromClass完成。

static void object_cxxDestructFromClass(id obj, Class cls){    void (*dtor)(id);    // Call cls's dtor first, then superclasses's dtors.    for ( ; cls; cls = cls->superclass) {        if (!cls->hasCxxDtor()) return;         dtor = (void(*)(id))            lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);        if (dtor != (void(*)(id))_objc_msgForward_impcache) {            if (PrintCxxCtors) {                _objc_inform("CXX: calling C++ destructors for class %s",                              cls->nameForLogging());            }            (*dtor)(obj);        }    }}复制代码

这段代码的意思就是沿着继承链逐层向上搜寻SEL_cxx_destruct这个selector,找到函数实现(void (*)(id)(函数指针)并执行。说白了就是找析构函数,并执行析构函数。

析构函数中书如何处理成员变量的?

  1. 对于strong来说执行objc_storeStrong(&ivar, nil)release旧对象,ivar赋新值nil;
  2. 对于weak来说执行objc_destroyWeak(&ivar)消除对象weak表中的ivar地址。

关于这个函数中也有提到。

用一张图表示dealloc的流程:

至于dealloc的调用时机,是跟引用计数器相关的。

转载地址:http://doqjl.baihongyu.com/

你可能感兴趣的文章
Kafka: Connect
查看>>
hibernate(七) hibernate中查询方式详解
查看>>
用gulp构建你的前端项目
查看>>
cmd for 循环拷贝文件
查看>>
【转】PHP date("Y-m-d H:i:s");获取当前时间 差8小时解决办法
查看>>
System.Security.Cryptography.CryptographicException,密钥集不存在
查看>>
敏捷团队中的QA由来
查看>>
gdb调试报错:Missing separate debuginfos, use: debuginfo-install glibc-XXX
查看>>
根据百度API获得经纬度,然后根据经纬度在获得城市信息
查看>>
mariadb 10.1查看per connection内存消耗
查看>>
重装MAC系统 “安装器有效负载签名检查失败” 解决方法
查看>>
(转) Supercharging Style Transfer
查看>>
JMeter性能测试,验证请求数据的准确性(wc命令)
查看>>
Python学习札记(二十三) 函数式编程4 sorted
查看>>
跟着百度学PHP[14]-PDO-优化驱动
查看>>
mysql的.sql文件头部 /*!32312 IF NOT EXISTS*/;
查看>>
ONVIF测试方法及工具
查看>>
《ArcGIS Runtime SDK for Android开发笔记》——数据制作篇:发布具有同步能力的FeatureService服务...
查看>>
Oracle快速克隆安装
查看>>
Spring Boot中使用JdbcTemplate访问数据库
查看>>