一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Objc 對(duì)象的今生今世

 最初九月雪 2017-06-09

前言

在面向?qū)ο缶幊讨?,我們每天都在?chuàng)建對(duì)象,用對(duì)象描述著整個(gè)世界,然而對(duì)象是如何從孕育到銷(xiāo)毀的呢?

目錄

  • 1.孕育對(duì)象
  • 2.對(duì)象的出生
  • 3.對(duì)象的成長(zhǎng)
  • 4.對(duì)象的銷(xiāo)毀
  • 5.總結(jié)
一.孕育對(duì)象

每天開(kāi)發(fā)我們都在alloc對(duì)象,而alloc方法做了些什么呢?


+ (id)alloc {
    return _objc_rootAlloc(self);
}

所有對(duì)象alloc都會(huì)調(diào)用這個(gè)root的方法


id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

這個(gè)方法又會(huì)去調(diào)用callAlloc方法


static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (checkNil && !cls) return nil;

#if __OBJC2__
    if (! cls->ISA()->hasCustomAWZ()) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (cls->canAllocFast()) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (!obj) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (!obj) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

由于入?yún)?checkNil = false,所以不會(huì)返回nil。


    bool hasCustomAWZ() {
        return ! bits.hasDefaultAWZ();
    }

這張圖,我們可以看到在對(duì)象的數(shù)據(jù)段data中,class_rw_t中有一個(gè)flags。


    bool hasDefaultAWZ( ) {
        return data()->flags & RW_HAS_DEFAULT_AWZ;
    }

#define RW_HAS_DEFAULT_AWZ    (1<<16)

RW_HAS_DEFAULT_AWZ 這個(gè)是用來(lái)標(biāo)示當(dāng)前的class或者是superclass是否有默認(rèn)的alloc/allocWithZone:。值得注意的是,這個(gè)值會(huì)存儲(chǔ)在metaclass 中。

hasDefaultAWZ( )方法是用來(lái)判斷當(dāng)前class是否有默認(rèn)的allocWithZone。

如果cls->ISA()->hasCustomAWZ()返回YES,意味著有默認(rèn)的allocWithZone方法,那么就直接對(duì)class進(jìn)行allocWithZone,申請(qǐng)內(nèi)存空間。


    if (allocWithZone) return [cls allocWithZone:nil];

allocWithZone會(huì)去調(diào)用rootAllocWithZone


+ (id)allocWithZone:(struct _NSZone *)zone {
    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}

接下來(lái)就仔細(xì)看看_objc_rootAllocWithZone的具體實(shí)現(xiàn)


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 || UseGC) {
        obj = class_createInstance(cls, 0);
    }
    else {
        obj = class_createInstanceFromZone(cls, 0, zone);
    }
#endif

    if (!obj) obj = callBadAllocHandler(cls);
    return obj;
}

在__OBJC2__中,直接調(diào)用class_createInstance(cls, 0);方法去創(chuàng)建對(duì)象。


id  class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

關(guān)于_class_createInstanceFromZone方法這里先不詳細(xì)分析,下面再詳細(xì)分析,先理清程序脈絡(luò)。

在objc的老版本中要先去看看zone是否有空間,是否用了垃圾回收,如果沒(méi)有空間,或者用了垃圾回收,就會(huì)調(diào)用class_createInstance(cls, 0)方法獲取對(duì)象,否則調(diào)用class_createInstanceFromZone(cls, 0, zone);獲取對(duì)象。


id class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
    return _class_createInstanceFromZone(cls, extraBytes, zone);
}

可以看到,創(chuàng)建對(duì)象最終調(diào)用的函數(shù)都是_class_createInstanceFromZone,不管objc的版本是新版還是舊版。

如果創(chuàng)建成功就返回objc,如果創(chuàng)建失敗,就會(huì)調(diào)用callBadAllocHandler方法。


static id callBadAllocHandler(Class cls)
{
    // fixme add re-entrancy protection in case allocation fails inside handler
    return (*badAllocHandler)(cls);
}

static id(*badAllocHandler)(Class) = &defaultBadAllocHandler;

static id defaultBadAllocHandler(Class cls)
{
    _objc_fatal("attempt to allocate object of class '%s' failed", 
                cls->nameForLogging());
}

創(chuàng)建對(duì)象失敗后,最終會(huì)調(diào)用_objc_fatal輸出"attempt to allocate object of class failed"創(chuàng)建對(duì)象失敗。

到此就完成了callAlloc中hasCustomAWZ( )返回YES的情況。那么hasCustomAWZ( )函數(shù)返回NO,情況是怎么樣的呢?


    if (! cls->ISA()->hasCustomAWZ()) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (cls->canAllocFast()) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (!obj) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (!obj) return callBadAllocHandler(cls);
            return obj;
        }
    }

這一段是hasCustomAWZ( )返回NO的情況,對(duì)應(yīng)的是當(dāng)前class沒(méi)有默認(rèn)的allocWithZone的情況。

在沒(méi)有默認(rèn)的allocWithZone的情況下,還需要再次判斷當(dāng)前的class是否支持快速alloc。如果可以,直接調(diào)用calloc函數(shù),申請(qǐng)1塊bits.fastInstanceSize()大小的內(nèi)存空間,如果創(chuàng)建失敗,也會(huì)調(diào)用callBadAllocHandler函數(shù)。

如果創(chuàng)建成功,就去初始化Isa指針和dtor。


    bool hasCxxDtor() {
        return data()->flags & RW_HAS_CXX_DTOR;
    }

// class or superclass has .cxx_destruct implementation
#define RW_HAS_CXX_DTOR       (1<<17)

dtor是用來(lái)判斷當(dāng)前class或者superclass是否有.cxx_destruct函數(shù)的實(shí)現(xiàn)。

如果當(dāng)前的class不支持快速alloc,那么就乖乖的去調(diào)用class_createInstance(cls, 0);方法去創(chuàng)建一個(gè)新的對(duì)象。

小結(jié)一下:

經(jīng)過(guò)上面的一系列判斷,“孕育對(duì)象”的過(guò)程最終落在了_class_createInstanceFromZone函數(shù)上了。


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());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocIndexed();

    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!UseGC  &&  !zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
#if SUPPORT_GC
        if (UseGC) {
            obj = (id)auto_zone_allocate_object(gc_zone, size,
                                                AUTO_OBJECT_SCANNED, 0, 1);
        } else 
#endif
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use non-indexed isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}

ctor 和 dtor 分別是什么呢?


    bool hasCxxCtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxCtor();
    }

    bool hasCxxCtor() {
        return data()->flags & RW_HAS_CXX_CTOR;
    }

#define RW_HAS_CXX_CTOR       (1<<18)

ctor是判斷當(dāng)前class或者superclass 是否有.cxx_construct構(gòu)造方法的實(shí)現(xiàn)。


    bool hasCxxDtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxDtor();
    }

    bool hasCxxDtor() {
        return data()->flags & RW_HAS_CXX_DTOR;
    }

#define RW_HAS_CXX_DTOR       (1<<17)

dtor是判斷判斷當(dāng)前class或者superclass 是否有.cxx_destruct析構(gòu)方法的實(shí)現(xiàn)。


    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

實(shí)例大小 instanceSize會(huì)存儲(chǔ)在類(lèi)的 isa_t結(jié)構(gòu)體中,然后經(jīng)過(guò)對(duì)齊最后返回。

注意:Core Foundation 需要所有的對(duì)象的大小都必須大于或等于 16 字節(jié)。

在獲取對(duì)象大小之后,直接調(diào)用calloc函數(shù)就可以為對(duì)象分配內(nèi)存空間了。

關(guān)于calloc函數(shù)

The calloc( ) function contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero.

這個(gè)函數(shù)也是為什么我們申請(qǐng)出來(lái)的對(duì)象,初始值是0或者nil的原因。因?yàn)檫@個(gè)calloc( )函數(shù)會(huì)默認(rèn)的把申請(qǐng)出來(lái)的空間初始化為0或者nil。

申請(qǐng)完內(nèi)存空間之后,還需要再初始化Isa指針。


obj->initInstanceIsa(cls, hasCxxDtor);

obj->initIsa(cls);

初始化Isa指針有這上面兩個(gè)函數(shù)。


inline void  objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!UseGC);
    assert(!cls->requiresRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}


inline void  objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

從上述源碼中,我們也能看出,最終都是調(diào)用了initIsa函數(shù),只不過(guò)入?yún)⒉煌?/p>


inline void  objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 

    if (!indexed) {
        isa.cls = cls;
    } else {
        assert(!DisableIndexedIsa);
        isa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.indexed is part of ISA_MAGIC_VALUE
        isa.has_cxx_dtor = hasCxxDtor;
        isa.shiftcls = (uintptr_t)cls >> 3;
    }
}

初始化的過(guò)程就是對(duì)isa_t結(jié)構(gòu)體初始化的過(guò)程。


# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t indexed           : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };

具體初始化的過(guò)程請(qǐng)參見(jiàn)這篇神經(jīng)病院Objective-C Runtime入院第一天——isa和Class

將當(dāng)前地址右移三位的主要原因是用于將 Class 指針中無(wú)用的后三位清除減小內(nèi)存的消耗,因?yàn)轭?lèi)的指針要按照字節(jié)(8 bits)對(duì)齊內(nèi)存,其指針后三位都是沒(méi)有意義的 0。
絕大多數(shù)機(jī)器的架構(gòu)都是 byte-addressable 的,但是對(duì)象的內(nèi)存地址必須對(duì)齊到字節(jié)的倍數(shù),這樣可以提高代碼運(yùn)行的性能,在 iPhone5s 中虛擬地址為 33 位,所以用于對(duì)齊的最后三位比特為 000,我們只會(huì)用其中的 30 位來(lái)表示對(duì)象的地址。

至此,孕育對(duì)象的過(guò)程就完成了。

二.對(duì)象的出生

一旦當(dāng)我們調(diào)用init方法的時(shí)候,對(duì)象就會(huì)“出生”了。


- (id)init {
    return _objc_rootInit(self);
}

init會(huì)調(diào)用_objc_rootInit方法。


id _objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

而_objc_rootInit方法的作用也僅僅就是返回了當(dāng)前對(duì)象而已。

三.對(duì)象的生長(zhǎng)

關(guān)于對(duì)象的生長(zhǎng),其實(shí)是想談?wù)剬?duì)象初始化之后,訪問(wèn)它的屬性和方法,它們?cè)趦?nèi)存中的樣子。


#import <Foundation/Foundation.h>

@interface Student : NSObject
@property (strong , nonatomic) NSString *name;
+(void)study;
-(void)run;
@end


#import "Student.h"
@implementation Student

+(void)study
{
    NSLog(@"Study"); 
}

-(void)run
{
    NSLog(@"Run");
}
@end

這里我們新建一個(gè)Student類(lèi),來(lái)舉例說(shuō)明。這個(gè)類(lèi)很簡(jiǎn)單,只有一個(gè)name的屬性,加上一個(gè)類(lèi)方法,和一個(gè)實(shí)例方法。


        Student  *stu = [[Student alloc]init];

        NSLog(@"Student's class is %@", [stu class]);
        NSLog(@"Student's meta class is %@", object_getClass([stu class]));
        NSLog(@"Student's meta class's superclass is %@", object_getClass(object_getClass([stu class])));

        Class currentClass = [Student class];
        for (int i = 1; i < 5; i++)
        {
            NSLog(@"Following the isa pointer %d times gives %p %@", i, currentClass,currentClass);
            currentClass = object_getClass(currentClass);
        }

        NSLog(@"NSObject's class is %p", [NSObject class]);
        NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));

寫(xiě)出上述的代碼,分析一下結(jié)構(gòu)。

輸出如下:



Student's class is Student
Student's meta class is Student
Student's meta class's superclass is NSObject
Following the isa pointer 1 times gives 0x100004d90 Student
Following the isa pointer 2 times gives 0x100004d68 Student
Following the isa pointer 3 times gives 0x7fffba0b20f0 NSObject
Following the isa pointer 4 times gives 0x7fffba0b20f0 NSObject
NSObject's class is 0x7fffba0b2140
NSObject's meta class is 0x7fffba0b20f0

經(jīng)過(guò)上面的打印結(jié)果,我們可以知道,一個(gè)類(lèi)的實(shí)例的isa是指向它的class,如下圖:

一個(gè)類(lèi)的實(shí)例,虛線指向灰色的區(qū)域,灰色的區(qū)域是一個(gè)Class pair,里面包含兩個(gè)東西,一個(gè)是類(lèi),另一個(gè)是meta-class。類(lèi)的isa指向meta-class。由于student是繼承NSObject,所以Student的class的meta-class的superclass是NSObject。

為了弄清楚這3個(gè)東西里面分別存了些什么,我們進(jìn)一步的打印一些信息。



+ (NSArray *)instanceVariables {
    unsigned int outCount;
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0; i < outCount; i++) {
        NSString *type = [NSString decodeType:ivar_getTypeEncoding(ivars[i])];
        NSString *name = [NSString stringWithCString:ivar_getName(ivars[i]) encoding:NSUTF8StringEncoding];
        NSString *ivarDescription = [NSString stringWithFormat:@"%@ %@", type, name];
        [result addObject:ivarDescription];
    }
    free(ivars);
    return result.count ? [result copy] : nil;
}

從之前的打印信息我們能知道,0x100004d90是類(lèi)的地址。0x100004d68是meta-class類(lèi)的地址。


po [0x100004d90 instanceVariables]
po [0x100004d68 instanceVariables]

打印出來(lái):



<__NSSingleObjectArrayI 0x100302460>(
  NSString* _name
)

nil

從這里就知道了,屬性這些是存儲(chǔ)在類(lèi)中。

接下來(lái)就是關(guān)于類(lèi)方法和實(shí)例方法的認(rèn)識(shí),+號(hào)方法和-號(hào)方法的認(rèn)識(shí)。

在內(nèi)存中其實(shí)沒(méi)有+號(hào)和-號(hào)方法的概念。做個(gè)試驗(yàn):


+ (NSArray *)ClassMethodNames
{
    NSMutableArray * array = [NSMutableArray array];
    unsigned int methodCount = 0;
    Method * methodList = class_copyMethodList([self class], &methodCount);
    unsigned int i;
    for(i = 0; i < methodCount; i++) {
        [array addObject: NSStringFromSelector(method_getName(methodList[i]))];
    }

    free(methodList);
    return array;
}

po [0x100004d90 ClassMethodNames]
po [0x100004d68 ClassMethodNames]

打印出來(lái):


<__NSArrayM 0x100303310>(
.cxx_destruct,
name,
setName:,
run
)

<__NSArrayM 0x100303800>(
study
)

0x100004d90是類(lèi)對(duì)象,里面存儲(chǔ)的是-號(hào)方法,還有另外3個(gè)方法,getter,setter,還有.cxx_destruct方法

0x100004d68是meta-class,里面存儲(chǔ)的是+號(hào)方法。

當(dāng)然在runtime的meta-class有一處很特殊,那就是NSObject的meta-class,它的superclass是它自己本身。為了防止調(diào)用NSObject協(xié)議里面的減號(hào)方法可能會(huì)出現(xiàn)崩潰,比如copy的-號(hào)方法,于是在NSObject的meta-class里面把所有的NSObject的+號(hào)方法都重新實(shí)現(xiàn)了一遍,就是為了消息傳遞到這里,攔截了一遍。所以一般NSObject協(xié)議方法同一個(gè)方法都有+號(hào)和-號(hào)方法。

值得說(shuō)明的是,class和meta-class都是單例。

關(guān)于對(duì)象,所有的對(duì)象在內(nèi)存里面都有一個(gè)isa,isa就是一個(gè)小“雷達(dá)”,有了它,就可以在runtime下給一個(gè)對(duì)象發(fā)送消息了。

所以對(duì)象的實(shí)質(zhì):Objc中的對(duì)象是一個(gè)指向ClassObject地址的變量,即 id obj = &ClassObject 。

關(guān)于對(duì)象的屬性實(shí)質(zhì)是,void *ivar = &obj + offset(N)


    NSString *myName = @"halfrost";
    NSLog(@"myName 地址 = %p , 大小 = %lu  ",&myName ,sizeof(myName));

    id cls = [Student class];
    NSLog(@"Student class = %@ 地址 = %p , 大小 = %lu", cls, &cls,sizeof(cls));

    void *obj = &cls;
    NSLog(@"Void *obj = %@ 地址 = %p , 大小 = %lu", obj,&obj, sizeof(obj));

    NSLog(@"%@  %p",((__bridge Student *)obj).name,((__bridge Student *)obj).name);

輸出



myName 地址 = 0x7fff562eeaa8 , 大小 = 8  
Student class = Student 地址 = 0x7fff562eeaa0 , 大小 = 8
Void *obj = <Student: 0x7fff562eeaa0> 地址 = 0x7fff562eea98 , 大小 = 8
halfrost  0x10a25c068

從這個(gè)例子就可以說(shuō)明,對(duì)象的實(shí)質(zhì)就是指向類(lèi)對(duì)象的地址變量,從上面例子里面obj就可以看出, id obj = &ClassObject ,cls是Student的類(lèi)對(duì)象,所以obj是Student的對(duì)象。

類(lèi)對(duì)象是在main函數(shù)執(zhí)行之前就加載進(jìn)內(nèi)存的,可執(zhí)行文件中和動(dòng)態(tài)庫(kù)所有的符號(hào)(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被 runtime 所管理,再這之后,runtime 的那些方法(動(dòng)態(tài)添加 Class、swizzle 等等才能生效)

具體可以看這篇文章iOS 程序 main 函數(shù)之前發(fā)生了什么

還是回到例子中來(lái),關(guān)于對(duì)象的屬性,就是obj的地址加上偏移量,就可以訪問(wèn)到,上述的例子中,obj地址是0x7fff562eea98,往下偏移8,到了class的地址,0x7fff562eeaa0,再往下偏移8,就到了name屬性的地址,0x7fff562eeaa8。在name中存儲(chǔ)的是字符串的首地址,根據(jù)打印信息也看到了,存儲(chǔ)的是一個(gè)指針,指向的0x10a25c068的地址。

如果我們打印一下這個(gè)地址:

就會(huì)發(fā)現(xiàn)里面存的就是我們的字符串。

總結(jié)一下就是上面這張圖,每個(gè)對(duì)象的isa都存的是Class的內(nèi)存地址,Class是在main函數(shù)執(zhí)行之前就加載進(jìn)內(nèi)存的,并且由Runtime所管理。所以只需要構(gòu)造一個(gè)指向Class的指針,即isa,就可以成為一個(gè)對(duì)象。

而對(duì)象的屬性,就是在對(duì)象的首地址上進(jìn)行的偏移。如上圖,當(dāng)知道對(duì)象的首地址是0x7fff562eea98,那么偏移8個(gè)字節(jié)就到了isa,再偏移8個(gè)字節(jié)就到了name屬性了。對(duì)象的屬性就是在內(nèi)存中偏移尋址取值的過(guò)程。

四.對(duì)象的銷(xiāo)毀

對(duì)象的銷(xiāo)毀就是調(diào)用dealloc方法。



- (void)dealloc {
    _objc_rootDealloc(self);
}

dealloc方法會(huì)調(diào)用_objc_rootDealloc方法


void _objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}


inline void objc_object::rootDealloc()
{
    assert(!UseGC);
    if (isTaggedPointer()) return;

    if (isa.indexed  &&  
        !isa.weakly_referenced  &&  
        !isa.has_assoc  &&  
        !isa.has_cxx_dtor  &&  
        !isa.has_sidetable_rc)
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

如果是TaggedPointer,直接return。

indexed是代表是否開(kāi)啟isa指針優(yōu)化。weakly_referenced代表對(duì)象被指向或者曾經(jīng)指向一個(gè) ARC 的弱變量。has_assoc代表對(duì)象含有或者曾經(jīng)含有關(guān)聯(lián)引用。has_cxx_dtor之前提到過(guò)了,是析構(gòu)器。has_sidetable_rc判斷該對(duì)象的引用計(jì)數(shù)是否過(guò)大。



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

    objc_destructInstance(obj);

#if SUPPORT_GC
    if (UseGC) {
        auto_zone_retain(gc_zone, obj); // gc free expects rc==1
    }
#endif

    free(obj);

    return nil;
}

object_dispose會(huì)調(diào)用objc_destructInstance。



/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 
* Calls C++ destructors.
* Calls ARR ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.
* CoreFoundation and other clients do call this under GC.
**********************************************************************/
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = !UseGC && obj->hasAssociatedObjects();
        bool dealloc = !UseGC;

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        if (dealloc) obj->clearDeallocating();
    }

    return obj;
}

銷(xiāo)毀一個(gè)對(duì)象,靠的是底層的C++析構(gòu)函數(shù)完成的。還需要移除associative的引用。

接下來(lái)就依次詳細(xì)看看銷(xiāo)毀對(duì)象的3個(gè)方法。

1.object_cxxDestruct


void object_cxxDestruct(id obj)
{
    if (!obj) return;
    if (obj->isTaggedPointer()) return;
    object_cxxDestructFromClass(obj, obj->ISA());
}

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);
        }
    }
}

從子類(lèi)開(kāi)始沿著繼承鏈一直找到父類(lèi),向上搜尋SEL_cxx_destruct
這個(gè)selector,找到函數(shù)實(shí)現(xiàn)(void (*)(id)(函數(shù)指針)并執(zhí)行。

以下引用ARC下dealloc過(guò)程及.cxx_destruct的探究的內(nèi)容:

這篇文章中:

ARC actually creates a -.cxx_destruct method to handle freeing instance variables. This method was originally created for calling C++ destructors automatically when an object was destroyed.

和《Effective Objective-C 2.0》中提到的:

When the compiler saw that an object contained C++ objects, it would generate a method called .cxx_destruct. ARC piggybacks on this method and emits the required cleanup code within it.

可以了解到,.cxx_destruct方法原本是為了C++對(duì)象析構(gòu)的,ARC借用了這個(gè)方法插入代碼實(shí)現(xiàn)了自動(dòng)內(nèi)存釋放的工作。

在ARC中dealloc方法在最后一次release后被調(diào)用,但此時(shí)實(shí)例變量(Ivars)并未釋放,父類(lèi)的dealloc的方法將在子類(lèi)dealloc方法返回后自動(dòng)調(diào)用。ARC下對(duì)象的實(shí)例變量在根類(lèi)[NSObject dealloc]中釋放(通常root class都是NSObject),變量釋放順序各種不確定(一個(gè)類(lèi)內(nèi)的不確定,子類(lèi)和父類(lèi)間也不確定,也就是說(shuō)不用care釋放順序)

經(jīng)過(guò)@sunnyxx文中的研究:
1.ARC下對(duì)象的成員變量于編譯器插入的.cxx_desctruct方法自動(dòng)釋放。
2.ARC下[super dealloc]方法也由編譯器自動(dòng)插入。

至于.cxx_destruct方法的實(shí)現(xiàn),還請(qǐng)看@sunnyxx 那篇文章里面詳細(xì)的分析。

2._object_remove_assocations

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

在移除關(guān)聯(lián)對(duì)象object的時(shí)候,會(huì)先去判斷object的isa_t中的第二位has_assoc的值,當(dāng)object 存在并且object->hasAssociatedObjects( )值為1的時(shí)候,才會(huì)去調(diào)用_object_remove_assocations方法。

_object_remove_assocations方法的目的是刪除第二張ObjcAssociationMap表,即刪除所有的關(guān)聯(lián)對(duì)象。刪除第二張表,就需要在第一張AssociationsHashMap表中遍歷查找。這里會(huì)把第二張ObjcAssociationMap表中所有的ObjcAssociation對(duì)象都存到一個(gè)數(shù)組elements里面,然后調(diào)用associations.erase( )刪除第二張表。最后再遍歷elements數(shù)組,把ObjcAssociation對(duì)象依次釋放。

這里移除的方式和Associated Object關(guān)聯(lián)對(duì)象里面的remove方法是完全一樣的。

3.clearDeallocating( )


inline void objc_object::clearDeallocating()
{
    if (!isa.indexed) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (isa.weakly_referenced  ||  isa.has_sidetable_rc) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}

這里涉及到了2個(gè)clear函數(shù),接下來(lái)一個(gè)個(gè)的看。



void objc_object::sidetable_clearDeallocating()
{
    SideTable& table = SideTables()[this];

    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        table.refcnts.erase(it);
    }
    table.unlock();
}

遍歷SideTable,循環(huán)調(diào)用weak_clear_no_lock函數(shù)。

weakly_referenced代表對(duì)象被指向或者曾經(jīng)指向一個(gè) ARC 的弱變量。has_sidetable_rc判斷該對(duì)象的引用計(jì)數(shù)是否過(guò)大。如果其中有一個(gè)為YES,則調(diào)用clearDeallocating_slow()方法。



// Slow path of clearDeallocating() 
// for objects with indexed isa
// that were ever weakly referenced 
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void objc_object::clearDeallocating_slow()
{
    assert(isa.indexed  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

clearDeallocating_slow也會(huì)最終調(diào)用weak_clear_no_lock方法。



/** 
 * Called by dealloc; nils out all weak pointers that point to the 
 * provided object so that they can no longer be used.
 * 
 * @param weak_table 
 * @param referent The object being deallocated. 
 */
void  weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;

    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;

    if (entry->out_of_line) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }

    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }

    weak_entry_remove(weak_table, entry);
}

這個(gè)函數(shù)會(huì)在weak_table中,清空引用計(jì)數(shù)表并清除弱引用表,將所有weak引用指nil。

總結(jié)

這篇文章詳細(xì)的分析了objc對(duì)象 從 出生 到 最終銷(xiāo)毀,它的今生今世全部在此。還請(qǐng)大家多多指點(diǎn)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    国产午夜精品福利免费不| 国产免费无遮挡精品视频| 日本欧美一区二区三区就| 欧美日韩少妇精品专区性色| 大香蕉伊人精品在线观看| 小草少妇视频免费看视频| 精品国产亚洲一区二区三区| 国产av大片一区二区三区| 国产成人人人97超碰熟女| 成人国产一区二区三区精品麻豆| 国产精品一区二区三区黄色片| 麻豆tv传媒在线观看| 亚洲一区二区三在线播放| 欧美日韩乱一区二区三区| 五月激情婷婷丁香六月网| 在线欧洲免费无线码二区免费| 午夜视频成人在线免费| 午夜久久久精品国产精品| 中国美女草逼一级黄片视频| 精品一区二区三区三级视频| 国产亚洲系列91精品| 黄片美女在线免费观看| 国产成人午夜av一区二区 | 国内九一激情白浆发布| 欧美成人高清在线播放| 综合久综合久综合久久| 激情五月激情婷婷丁香| 中字幕一区二区三区久久蜜桃| 色鬼综合久久鬼色88| 午夜免费精品视频在线看| 视频一区二区 国产精品| 清纯少妇被捅到高潮免费观看| 日韩一本不卡在线观看| 久久香蕉综合网精品视频| 91麻豆精品欧美一区| 国产欧美亚洲精品自拍| 高潮日韩福利在线观看| 成人国产激情福利久久| 91麻豆精品欧美视频| 国产日韩久久精品一区| 精品精品国产欧美在线|