在面向對象編程中,咱們天天都在建立對象,用對象描述着整個世界,然而對象是如何從孕育到銷燬的呢?git
天天開發咱們都在alloc對象,而alloc方法作了些什麼呢?github
+ (id)alloc {
return _objc_rootAlloc(self);
}複製代碼
全部對象alloc都會調用這個root的方法objective-c
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}複製代碼
這個方法又會去調用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];
}複製代碼
因爲入參 checkNil = false,因此不會返回nil。vim
bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}複製代碼
在這張圖,咱們能夠看到在對象的數據段data中,class_rw_t中有一個flags。api
bool hasDefaultAWZ( ) {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
#define RW_HAS_DEFAULT_AWZ (1<<16)複製代碼
RW_HAS_DEFAULT_AWZ 這個是用來標示當前的class或者是superclass是否有默認的alloc/allocWithZone:。值得注意的是,這個值會存儲在metaclass 中。數組
hasDefaultAWZ( )方法是用來判斷當前class是否有默認的allocWithZone。架構
若是cls->ISA()->hasCustomAWZ()返回YES,意味着有默認的allocWithZone方法,那麼就直接對class進行allocWithZone,申請內存空間。app
if (allocWithZone) return [cls allocWithZone:nil];複製代碼
allocWithZone會去調用rootAllocWithZoneide
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}複製代碼
接下來就仔細看看_objc_rootAllocWithZone的具體實現
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__中,直接調用class_createInstance(cls, 0);方法去建立對象。
id class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}複製代碼
關於_class_createInstanceFromZone方法這裏先不詳細分析,下面再詳細分析,先理清程序脈絡。
在objc的老版本中要先去看看zone是否有空間,是否用了垃圾回收,若是沒有空間,或者用了垃圾回收,就會調用class_createInstance(cls, 0)方法獲取對象,不然調用class_createInstanceFromZone(cls, 0, zone);獲取對象。
id class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
{
return _class_createInstanceFromZone(cls, extraBytes, zone);
}複製代碼
能夠看到,建立對象最終調用的函數都是_class_createInstanceFromZone,無論objc的版本是新版仍是舊版。
若是建立成功就返回objc,若是建立失敗,就會調用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());
}複製代碼
建立對象失敗後,最終會調用_objc_fatal輸出"attempt to allocate object of class failed"建立對象失敗。
到此就完成了callAlloc中hasCustomAWZ( )返回YES的狀況。那麼hasCustomAWZ( )函數返回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的狀況,對應的是當前class沒有默認的allocWithZone的狀況。
在沒有默認的allocWithZone的狀況下,還須要再次判斷當前的class是否支持快速alloc。若是能夠,直接調用calloc函數,申請1塊bits.fastInstanceSize()大小的內存空間,若是建立失敗,也會調用callBadAllocHandler函數。
若是建立成功,就去初始化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是用來判斷當前class或者superclass是否有.cxx_destruct函數的實現。
若是當前的class不支持快速alloc,那麼就乖乖的去調用class_createInstance(cls, 0);方法去建立一個新的對象。
小結一下:
通過上面的一系列判斷,「孕育對象」的過程最終落在了_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());
// 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是判斷當前class或者superclass 是否有.cxx_construct構造方法的實現。
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是判斷判斷當前class或者superclass 是否有.cxx_destruct析構方法的實現。
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;
}複製代碼
實例大小 instanceSize會存儲在類的 isa_t結構體中,而後通過對齊最後返回。
注意:Core Foundation 須要全部的對象的大小都必須大於或等於 16 字節。
在獲取對象大小以後,直接調用calloc函數就能夠爲對象分配內存空間了。
關於calloc函數
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.
這個函數也是爲何咱們申請出來的對象,初始值是0或者nil的緣由。由於這個calloc( )函數會默認的把申請出來的空間初始化爲0或者nil。
申請完內存空間以後,還須要再初始化Isa指針。
obj->initInstanceIsa(cls, hasCxxDtor);
obj->initIsa(cls);複製代碼
初始化Isa指針有這上面兩個函數。
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);
}複製代碼
從上述源碼中,咱們也能看出,最終都是調用了initIsa函數,只不過入參不一樣。
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;
}
}複製代碼
初始化的過程就是對isa_t結構體初始化的過程。
# 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) };複製代碼
具體初始化的過程請參見這篇神經病院Objective-C Runtime入院第一天——isa和Class
將當前地址右移三位的主要緣由是用於將 Class 指針中無用的後三位清除減少內存的消耗,由於類的指針要按照字節(8 bits)對齊內存,其指針後三位都是沒有意義的 0。
絕大多數機器的架構都是 byte-addressable 的,可是對象的內存地址必須對齊到字節的倍數,這樣能夠提升代碼運行的性能,在 iPhone5s 中虛擬地址爲 33 位,因此用於對齊的最後三位比特爲 000,咱們只會用其中的 30 位來表示對象的地址。
至此,孕育對象的過程就完成了。
一旦當咱們調用init方法的時候,對象就會「出生」了。
- (id)init {
return _objc_rootInit(self);
}複製代碼
init會調用_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方法的做用也僅僅就是返回了當前對象而已。
關於對象的生長,實際上是想談談對象初始化以後,訪問它的屬性和方法,它們在內存中的樣子。
#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複製代碼
這裏咱們新建一個Student類,來舉例說明。這個類很簡單,只有一個name的屬性,加上一個類方法,和一個實例方法。
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]));複製代碼
寫出上述的代碼,分析一下結構。
輸出以下:
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複製代碼
通過上面的打印結果,咱們能夠知道,一個類的實例的isa是指向它的class,以下圖:
一個類的實例,虛線指向灰色的區域,灰色的區域是一個Class pair,裏面包含兩個東西,一個是類,另外一個是meta-class。類的isa指向meta-class。因爲student是繼承NSObject,因此Student的class的meta-class的superclass是NSObject。
爲了弄清楚這3個東西里面分別存了些什麼,咱們進一步的打印一些信息。
+ (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是類的地址。0x100004d68是meta-class類的地址。
po [0x100004d90 instanceVariables]
po [0x100004d68 instanceVariables]複製代碼
打印出來:
<__NSSingleObjectArrayI 0x100302460>(
NSString* _name
)
nil複製代碼
從這裏就知道了,屬性這些是存儲在類中。
接下來就是關於類方法和實例方法的認識,+號方法和-號方法的認識。
在內存中其實沒有+號和-號方法的概念。作個試驗:
+ (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]複製代碼
打印出來:
<__NSArrayM 0x100303310>(
.cxx_destruct,
name,
setName:,
run
)
<__NSArrayM 0x100303800>(
study
)複製代碼
0x100004d90是類對象,裏面存儲的是-號方法,還有另外3個方法,getter,setter,還有.cxx_destruct方法
0x100004d68是meta-class,裏面存儲的是+號方法。
固然在runtime的meta-class有一處很特殊,那就是NSObject的meta-class,它的superclass是它本身自己。爲了防止調用NSObject協議裏面的減號方法可能會出現崩潰,好比copy的-號方法,因而在NSObject的meta-class裏面把全部的NSObject的+號方法都從新實現了一遍,就是爲了消息傳遞到這裏,攔截了一遍。因此通常NSObject協議方法同一個方法都有+號和-號方法。
值得說明的是,class和meta-class都是單例。
關於對象,全部的對象在內存裏面都有一個isa,isa就是一個小「雷達」,有了它,就能夠在runtime下給一個對象發送消息了。
因此對象的實質:Objc中的對象是一個指向ClassObject地址的變量,即 id obj = &ClassObject 。
關於對象的屬性實質是,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複製代碼
從這個例子就能夠說明,對象的實質就是指向類對象的地址變量,從上面例子裏面obj就能夠看出, id obj = &ClassObject ,cls是Student的類對象,因此obj是Student的對象。
類對象是在main函數執行以前就加載進內存的,可執行文件中和動態庫全部的符號(Class,Protocol,Selector,IMP,…)都已經按格式成功加載到內存中,被 runtime 所管理,再這以後,runtime 的那些方法(動態添加 Class、swizzle 等等才能生效)
具體能夠看這篇文章iOS 程序 main 函數以前發生了什麼
仍是回到例子中來,關於對象的屬性,就是obj的地址加上偏移量,就能夠訪問到,上述的例子中,obj地址是0x7fff562eea98,往下偏移8,到了class的地址,0x7fff562eeaa0,再往下偏移8,就到了name屬性的地址,0x7fff562eeaa8。在name中存儲的是字符串的首地址,根據打印信息也看到了,存儲的是一個指針,指向的0x10a25c068的地址。
若是咱們打印一下這個地址:
就會發現裏面存的就是咱們的字符串。
總結一下就是上面這張圖,每一個對象的isa都存的是Class的內存地址,Class是在main函數執行以前就加載進內存的,而且由Runtime所管理。因此只須要構造一個指向Class的指針,即isa,就能夠成爲一個對象。
而對象的屬性,就是在對象的首地址上進行的偏移。如上圖,當知道對象的首地址是0x7fff562eea98,那麼偏移8個字節就到了isa,再偏移8個字節就到了name屬性了。對象的屬性就是在內存中偏移尋址取值的過程。
對象的銷燬就是調用dealloc方法。
- (void)dealloc {
_objc_rootDealloc(self);
}複製代碼
dealloc方法會調用_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是表明是否開啓isa指針優化。weakly_referenced表明對象被指向或者曾經指向一個 ARC 的弱變量。has_assoc表明對象含有或者曾經含有關聯引用。has_cxx_dtor以前提到過了,是析構器。has_sidetable_rc判斷該對象的引用計數是否過大。
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會調用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;
}複製代碼
銷燬一個對象,靠的是底層的C++析構函數完成的。還須要移除associative的引用。
接下來就依次詳細看看銷燬對象的3個方法。
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);
}
}
}複製代碼
從子類開始沿着繼承鏈一直找到父類,向上搜尋SEL_cxx_destruct
這個selector,找到函數實現(void (*)(id)(函數指針)並執行。
如下引用ARC下dealloc過程及.cxx_destruct的探究的內容:
從這篇文章中:
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++對象析構的,ARC借用了這個方法插入代碼實現了自動內存釋放的工做。
在ARC中dealloc方法在最後一次release後被調用,但此時實例變量(Ivars)並未釋放,父類的dealloc的方法將在子類dealloc方法返回後自動調用。ARC下對象的實例變量在根類[NSObject dealloc]中釋放(一般root class都是NSObject),變量釋放順序各類不肯定(一個類內的不肯定,子類和父類間也不肯定,也就是說不用care釋放順序)
通過@sunnyxx文中的研究:
1.ARC下對象的成員變量於編譯器插入的.cxx_desctruct方法自動釋放。
2.ARC下[super dealloc]方法也由編譯器自動插入。
至於.cxx_destruct方法的實現,還請看@sunnyxx 那篇文章裏面詳細的分析。
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());
}複製代碼
在移除關聯對象object的時候,會先去判斷object的isa_t中的第二位has_assoc的值,當object 存在而且object->hasAssociatedObjects( )值爲1的時候,纔會去調用_object_remove_assocations方法。
_object_remove_assocations方法的目的是刪除第二張ObjcAssociationMap表,即刪除全部的關聯對象。刪除第二張表,就須要在第一張AssociationsHashMap表中遍歷查找。這裏會把第二張ObjcAssociationMap表中全部的ObjcAssociation對象都存到一個數組elements裏面,而後調用associations.erase( )刪除第二張表。最後再遍歷elements數組,把ObjcAssociation對象依次釋放。
這裏移除的方式和Associated Object關聯對象裏面的remove方法是徹底同樣的。
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個clear函數,接下來一個個的看。
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,循環調用weak_clear_no_lock函數。
weakly_referenced表明對象被指向或者曾經指向一個 ARC 的弱變量。has_sidetable_rc判斷該對象的引用計數是否過大。若是其中有一個爲YES,則調用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也會最終調用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);
}複製代碼
這個函數會在weak_table中,清空引用計數表並清除弱引用表,將全部weak引用指nil。
這篇文章詳細的分析了objc對象 從 出生 到 最終銷燬,它的此生今世所有在此。還請你們多多指點。