OC内存管理--对象的生成与销毁

栏目: Objective-C · 发布时间: 5年前

内容简介:原文链接在iOS开发中了,我们每天都会使用整个过程其实就是

原文链接 OC内存管理--对象的生成与销毁

在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 id
callAlloc(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_createInstance
id class_createInstance(Class cls, size_t extraBytes) {
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

//class_createInstanceFromZone
id 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的实现过程已经结束了,根据上面的源码分析,用一张图表示上述过程:

OC内存管理--对象的生成与销毁

这里可能会有个疑问,既然 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 void
objc_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 分为三种情况:

object_dispose

objc_destructInstance

我们先看 object_dispose 函数的源码:

id object_dispose(id obj) {
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
复制代码

做了两件事情:

objc_destructInstance

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 做了三件事情:

object_cxxDestruct
_object_remove_assocations
clearDeallocating

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)(函数指针)并执行。说白了就是找析构函数,并执行析构函数。

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

objc_storeStrong(&ivar, nil)
objc_destroyWeak(&ivar)

关于这个函数 Sunnyxx ARC下dealloc过程及.cxx_destruct的探究 中也有提到。

用一张图表示 dealloc 的流程:

OC内存管理--对象的生成与销毁

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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

TCP/IP网络管理

TCP/IP网络管理

亨特 / 电子工业 / 2006年3月1日 / 79.00元

本书是一本架设与维护TCP/IP网络的完整指南,无论你是在职的系统管理员,还是需要访问Internet的家用系统用户,都可从本书获得帮助。本书还讨论了高级路由协议(RIPv2、OSPF、BGP),以及实现这些协议的gated软件。对于各种重要的网络服务,如DNS,Apache,sendmail,Samba,PPP和DHCP,本书都提供了配置范例,以及相关的软件包与工具的语法参考。一起来看看 《TCP/IP网络管理》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具