對象的本質(zhì)
NSObject * obj = [[NSObject alloc] init];
轉(zhuǎn)化成c++可以到本質(zhì)是一個結(jié)構(gòu)體
struct NSObject_IMPL{
Class isa.
};
typedef struct objc_class * Class;
可以看出isa 是指向objc_class結(jié)構(gòu)的指針
obj對象的地址,就是結(jié)構(gòu)體的地址,結(jié)構(gòu)體里面第一個元素就是isa,結(jié)構(gòu)體的地址也就是isa指針的地址.
總結(jié)對象的本質(zhì)就是一個指向類結(jié)構(gòu)體的指針 我們NSLog(@"%@", obj)其實是打印了一個 指向NSObject這個類的指針的地址.
下面是一個Student轉(zhuǎn)成的c++
struct Student_IMPL{
struct NSObject_IMPL NSObject_IVARS;
int _num
int _age
};

struct lc_struct {
Class isa;
};
/*
if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
*/
LCKVCtest *kvc = [[LCKVCtest alloc]init];
Class class = kvc.class;
Class mClass = object_getClass(class);
Class mObjectClass = object_getClass([NSObject class]);
Class mObjectClass2 = object_getClass(mObjectClass);
NSLog(@"kvc = %p",kvc); //對象指向的地址
NSLog(@"class = %p",class); //類對象指向的地址
NSLog(@"mClass = %p",mClass);//元類對象指向的地址
NSLog(@"mObjectClass = %p",mObjectClass);//NSObject元類對象指向的地址
NSLog(@"mObjectClass = %p",mObjectClass2);//NSObject元類對象指向的地址
struct lc_struct *aclass = (__bridge struct lc_struct *)(class);
struct lc_struct *amClass = (__bridge struct lc_struct *)(mClass);
struct lc_struct *amObjectClass = (__bridge struct lc_struct *)(mObjectClass);
2020-01-13 14:28:47.635802+0800 測試[9928:1196495] kvc = 0x6000010d1460
2020-01-13 14:28:47.635989+0800 測試[9928:1196495] class = 0x105c37a48
2020-01-13 14:28:47.636143+0800 測試[9928:1196495] mClass = 0x105c37a20
2020-01-13 14:28:47.636298+0800 測試[9928:1196495] mObjectClass = 0x7fff89cc4558
2020-01-13 14:28:47.636454+0800 測試[9928:1196495] mObjectClass = 0x7fff89cc4558
(lldb) p/x kvc 得到kvc對象的地址
(LCKVCtest *) $13 = 0x00006000010d1460
(lldb) p/x kvc->isa //得到kvc結(jié)構(gòu)體中isa實例變量指向的地址
(Class) $14 = 0x0000000105c37a48 LCKVCtest
(lldb) p/x 0x0000000105c37a48 & 0x0000000ffffffff8ULL // 對象中的isa& ISA_MASK得到類的地址
(unsigned long long) $15 = 0x0000000105c37a48
(lldb) p/x &(kvc->isa) 得到結(jié)構(gòu)體isa的地址(isa是結(jié)構(gòu)體中第一個成員變量所以也是結(jié)構(gòu)體的地址也就是對象的地址)
(__unsafe_unretained Class *) $16 = 0x00006000010d1460
(lldb)
(lldb) p/x aclass->isa
(Class) $24 = 0x0000000105c37a20
(lldb) p/x 0x0000000105c37a20 & 0x0000000ffffffff8ULL
(unsigned long long) $25 = 0x0000000105c37a20
(lldb) p/x amClass->isa
(Class) $26 = 0x00007fff89cc4558
(lldb) p/x 0x00007fff89cc4558 & 0x0000000ffffffff8ULL
(unsigned long long) $27 = 0x0000000f89cc4558
(lldb) p/x amObjectClass->isa
(Class) $28 = 0x00007fff89cc4558
(lldb) p/x 0x00007fff89cc4558 & 0x0000000ffffffff8ULL
(unsigned long long) $29 = 0x0000000f89cc4558
(lldb)
我們吧Class對象轉(zhuǎn)正我們自定的結(jié)構(gòu)體指針類對象中isa& ISA_MASK指向元類對象 元類對象的中的isa& ISA_MASK指向NSObejc元類,NSObejc元類中的isa指向自己. (不用& ISA_MASK)
描述對象類和元類的關(guān)系這里來個張神圖鎮(zhèn)場再合適不過了

objc_class的布局詳細說明每個變量代表的含義
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
這里是類對象的內(nèi)存布局 但是遺憾的是!__OBJC2__ 已經(jīng)不是現(xiàn)在的版本了.
現(xiàn)在的版本是__OBJC2__
并且OBJC2_UNAVAILABLE 說明在__OBJC2__版本下這個結(jié)構(gòu)體也是不可用的,我們可以簡單分析一下
isa 可以簡單的人為是指向元類(其實需要$ISA_MASK)
super_class 指向父類
ivars 是成員變量列表
objc_method_list 方法列表
cache 方法緩存
protocols 協(xié)議類表
現(xiàn)在的class 結(jié)構(gòu)如下
//對象結(jié)構(gòu)體
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
void setInfo(uint32_t set) {
assert(isFuture() || isRealized());
data()->setFlags(set);
}
void clearInfo(uint32_t clear) {
assert(isFuture() || isRealized());
data()->clearFlags(clear);
}
// set and clear must not overlap
void changeInfo(uint32_t set, uint32_t clear) {
assert(isFuture() || isRealized());
assert((set & clear) == 0);
data()->changeFlags(set, clear);
}
bool hasCustomRR() {
return ! bits.hasDefaultRR();
}
void setHasDefaultRR() {
assert(isInitializing());
bits.setHasDefaultRR();
}
void setHasCustomRR(bool inherited = false);
void printCustomRR(bool inherited);
bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}
void setHasDefaultAWZ() {
assert(isInitializing());
bits.setHasDefaultAWZ();
}
void setHasCustomAWZ(bool inherited = false);
void printCustomAWZ(bool inherited);
bool instancesRequireRawIsa() {
return bits.instancesRequireRawIsa();
}
void setInstancesRequireRawIsa(bool inherited = false);
void printInstancesRequireRawIsa(bool inherited);
bool canAllocNonpointer() {
assert(!isFuture());
return !instancesRequireRawIsa();
}
bool canAllocFast() {
assert(!isFuture());
return bits.canAllocFast();
}
bool hasCxxCtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxCtor();
}
void setHasCxxCtor() {
bits.setHasCxxCtor();
}
bool hasCxxDtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxDtor();
}
void setHasCxxDtor() {
bits.setHasCxxDtor();
}
bool isSwift() {
return bits.isSwift();
}
// Return YES if the class's ivars are managed by ARC,
// or the class is MRC but has ARC-style weak ivars.
bool hasAutomaticIvars() {
return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);
}
// Return YES if the class's ivars are managed by ARC.
bool isARC() {
return data()->ro->flags & RO_IS_ARC;
}
#if SUPPORT_NONPOINTER_ISA
// Tracked in non-pointer isas; not tracked otherwise
#else
bool instancesHaveAssociatedObjects() {
// this may be an unrealized future class in the CF-bridged case
assert(isFuture() || isRealized());
return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;
}
void setInstancesHaveAssociatedObjects() {
// this may be an unrealized future class in the CF-bridged case
assert(isFuture() || isRealized());
setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
}
#endif
bool shouldGrowCache() {
return true;
}
void setShouldGrowCache(bool) {
// fixme good or bad for memory use?
}
bool isInitializing() {
return getMeta()->data()->flags & RW_INITIALIZING;
}
void setInitializing() {
assert(!isMetaClass());
ISA()->setInfo(RW_INITIALIZING);
}
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
void setInitialized();
bool isLoadable() {
assert(isRealized());
return true; // any class registered for +load is definitely loadable
}
IMP getLoadMethod();
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isRealized() {
return data()->flags & RW_REALIZED;
}
// Returns true if this is an unrealized future class.
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isFuture() {
return data()->flags & RW_FUTURE;
}
bool isMetaClass() {
assert(this);
assert(isRealized());
return data()->ro->flags & RO_META;
}
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
bool isRootClass() {
return superclass == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this;
}
const char *mangledName() {
// fixme can't assert locks here
assert(this);
if (isRealized() || isFuture()) {
return data()->ro->name;
} else {
return ((const class_ro_t *)data())->name;
}
}
const char *demangledName(bool realize = false);
const char *nameForLogging();
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceStart() {
assert(isRealized());
return data()->ro->instanceStart;
}
// Class's instance start rounded up to a pointer-size boundary.
// This is used for ARC layout bitmaps.
uint32_t alignedInstanceStart() {
return word_align(unalignedInstanceStart());
}
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
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;
}
void setInstanceSize(uint32_t newSize) {
assert(isRealized());
if (newSize != data()->ro->instanceSize) {
assert(data()->flags & RW_COPIED_RO);
*const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
}
bits.setFastInstanceSize(newSize);
}
void chooseClassArrayIndex();
void setClassArrayIndex(unsigned Idx) {
bits.setClassArrayIndex(Idx);
}
unsigned classArrayIndex() {
return bits.classArrayIndex();
}
}
//class_rw_t 結(jié)構(gòu)體
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
private:
bool getBit(uintptr_t bit)
{
return bits & bit;
}
#if FAST_ALLOC
static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change)
{
if (change & FAST_ALLOC_MASK) {
if (((oldBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE) &&
((oldBits >> FAST_SHIFTED_SIZE_SHIFT) != 0))
{
oldBits |= FAST_ALLOC;
} else {
oldBits &= ~FAST_ALLOC;
}
}
return oldBits;
}
#else
static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change) {
return oldBits;
}
#endif
void setBits(uintptr_t set)
{
uintptr_t oldBits;
uintptr_t newBits;
do {
oldBits = LoadExclusive(&bits);
newBits = updateFastAlloc(oldBits | set, set);
} while (!StoreReleaseExclusive(&bits, oldBits, newBits));
}
void clearBits(uintptr_t clear)
{
uintptr_t oldBits;
uintptr_t newBits;
do {
oldBits = LoadExclusive(&bits);
newBits = updateFastAlloc(oldBits & ~clear, clear);
} while (!StoreReleaseExclusive(&bits, oldBits, newBits));
}
public:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
assert(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
bool hasDefaultRR() {
return getBit(FAST_HAS_DEFAULT_RR);
}
void setHasDefaultRR() {
setBits(FAST_HAS_DEFAULT_RR);
}
void setHasCustomRR() {
clearBits(FAST_HAS_DEFAULT_RR);
}
#if FAST_HAS_DEFAULT_AWZ
bool hasDefaultAWZ() {
return getBit(FAST_HAS_DEFAULT_AWZ);
}
void setHasDefaultAWZ() {
setBits(FAST_HAS_DEFAULT_AWZ);
}
void setHasCustomAWZ() {
clearBits(FAST_HAS_DEFAULT_AWZ);
}
#else
bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
void setHasDefaultAWZ() {
data()->setFlags(RW_HAS_DEFAULT_AWZ);
}
void setHasCustomAWZ() {
data()->clearFlags(RW_HAS_DEFAULT_AWZ);
}
#endif
#if FAST_HAS_CXX_CTOR
bool hasCxxCtor() {
return getBit(FAST_HAS_CXX_CTOR);
}
void setHasCxxCtor() {
setBits(FAST_HAS_CXX_CTOR);
}
#else
bool hasCxxCtor() {
return data()->flags & RW_HAS_CXX_CTOR;
}
void setHasCxxCtor() {
data()->setFlags(RW_HAS_CXX_CTOR);
}
#endif
#if FAST_HAS_CXX_DTOR
bool hasCxxDtor() {
return getBit(FAST_HAS_CXX_DTOR);
}
void setHasCxxDtor() {
setBits(FAST_HAS_CXX_DTOR);
}
#else
bool hasCxxDtor() {
return data()->flags & RW_HAS_CXX_DTOR;
}
void setHasCxxDtor() {
data()->setFlags(RW_HAS_CXX_DTOR);
}
#endif
#if FAST_REQUIRES_RAW_ISA
bool instancesRequireRawIsa() {
return getBit(FAST_REQUIRES_RAW_ISA);
}
void setInstancesRequireRawIsa() {
setBits(FAST_REQUIRES_RAW_ISA);
}
#elif SUPPORT_NONPOINTER_ISA
bool instancesRequireRawIsa() {
return data()->flags & RW_REQUIRES_RAW_ISA;
}
void setInstancesRequireRawIsa() {
data()->setFlags(RW_REQUIRES_RAW_ISA);
}
#else
bool instancesRequireRawIsa() {
return true;
}
void setInstancesRequireRawIsa() {
// nothing
}
#endif
#if FAST_ALLOC
size_t fastInstanceSize()
{
assert(bits & FAST_ALLOC);
return (bits >> FAST_SHIFTED_SIZE_SHIFT) * 16;
}
void setFastInstanceSize(size_t newSize)
{
// Set during realization or construction only. No locking needed.
assert(data()->flags & RW_REALIZING);
// Round up to 16-byte boundary, then divide to get 16-byte units
newSize = ((newSize + 15) & ~15) / 16;
uintptr_t newBits = newSize << FAST_SHIFTED_SIZE_SHIFT;
if ((newBits >> FAST_SHIFTED_SIZE_SHIFT) == newSize) {
int shift = WORD_BITS - FAST_SHIFTED_SIZE_SHIFT;
uintptr_t oldBits = (bits << shift) >> shift;
if ((oldBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE) {
newBits |= FAST_ALLOC;
}
bits = oldBits | newBits;
}
}
bool canAllocFast() {
return bits & FAST_ALLOC;
}
#else
size_t fastInstanceSize() {
abort();
}
void setFastInstanceSize(size_t) {
// nothing
}
bool canAllocFast() {
return false;
}
#endif
void setClassArrayIndex(unsigned Idx) {
#if SUPPORT_INDEXED_ISA
// 0 is unused as then we can rely on zero-initialisation from calloc.
assert(Idx > 0);
data()->index = Idx;
#endif
}
unsigned classArrayIndex() {
#if SUPPORT_INDEXED_ISA
return data()->index;
#else
return 0;
#endif
}
bool isSwift() {
return getBit(FAST_IS_SWIFT);
}
void setIsSwift() {
setBits(FAST_IS_SWIFT);
}
}
//cache
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
public:
struct bucket_t *buckets();
mask_t mask();
mask_t occupied();
void incrementOccupied();
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
void initializeToEmpty();
mask_t capacity();
bool isConstantEmptyCache();
bool canBeFreed();
static size_t bytesForCapacity(uint32_t cap);
static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
void expand();
void reallocate(mask_t oldCapacity, mask_t newCapacity);
struct bucket_t * find(cache_key_t key, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
以上是現(xiàn)在objc_class現(xiàn)在的內(nèi)存布局 我們摘出存在objc_class結(jié)構(gòu)體內(nèi)存里面的關(guān)鍵信息
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
......
}
在arm64架構(gòu)之前,isa就是一個普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
從arm64架構(gòu)開始,對isa進行了優(yōu)化,變成了一個共用體(union)結(jié)構(gòu),還使用位域來存儲更多的信息

nonpointer
0,代表普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
1,代表優(yōu)化過,使用位域存儲更多的信息
has_assoc
是否有設(shè)置過關(guān)聯(lián)對象,如果沒有,釋放時會更快
has_cxx_dtor
是否有C++的析構(gòu)函數(shù)(.cxx_destruct),如果沒有,釋放時會更快
shiftcls
存儲著Class、Meta-Class對象的內(nèi)存地址信息
magic
用于在調(diào)試時分辨對象是否未完成初始化
weakly_referenced
是否有被弱引用指向過,如果沒有,釋放時會更快
deallocating
對象是否正在釋放
extra_rc
里面存儲的值是引用計數(shù)器減1
has_sidetable_rc
引用計數(shù)器是否過大無法存儲在isa中
如果為1,那么引用計數(shù)會存儲在一個叫SideTable的類的屬性中
分類底層的實現(xiàn)
分類在runtime中結(jié)構(gòu)是這樣的我們每一個分類 都會生成一個這樣的結(jié)構(gòu)體
下面是分類在objc代碼源碼中的結(jié)構(gòu)體
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
#import "LCTest.h"
NS_ASSUME_NONNULL_BEGIN
@interface LCTest (aaaa)<NSCopying>
@property (nonatomic, strong) NSString *aaStr;
+ (void)aaajia;
- (void)aaajian;
@end
NS_ASSUME_NONNULL_END
#import "LCTest+aaaa.h"
@implementation LCTest (aaaa)
+ (void)aaajia{
}
- (void)aaajian{
}
- (id)copyWithZone:(NSZone *)zone{
return nil;
}
@end
我們用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc LCTest+aaaa.m 看看分類轉(zhuǎn)換成的c++代碼
static struct _category_t _OBJC_$_CATEGORY_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"LCTest",
0, // &OBJC_CLASS_$_LCTest,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LCTest_$_aaaa,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LCTest_$_aaaa,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_LCTest_$_aaaa,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LCTest_$_aaaa,
};
//這是傳換成的c++基本和runtime源碼中的一致
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
//存放實例方法的_method_list_t
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"aaajian", "v16@0:8", (void *)_I_LCTest_aaaa_aaajian},
{(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", (void *)_I_LCTest_aaaa_copyWithZone_}}
};
//存放類方法的_method_list_t
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"aaajia", "v16@0:8", (void *)_C_LCTest_aaaa_aaajia}}
};
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSCopying
};
//關(guān)于協(xié)議在分類中
struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};
struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
0,
"NSCopying",
0,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
0,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};
//屬性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"aaStr","T@\"NSString\",&,N"}}
};
struct _prop_t {
const char *name;
const char *attributes;
};
我們從上面可以看到一個類的一個分類底層其實是一個結(jié)構(gòu)體.
結(jié)構(gòu)體中包含的有
1類的 名字
2..
3實例方法列表
4對象方法類表
5協(xié)議類表
6屬性列表
我們知道一個對象調(diào)用一個實例方法尋找這個方法其實是在類里面的尋找的,如果我們調(diào)用一個分類的實例方法,是怎么找到這個方法的呢?
我們根據(jù)runtime的源碼分析一下
源碼解讀順序
objc-os.mm
_objc_init
map_images
map_images_nolock
objc-runtime-new.mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、 memcpy
// 一行一行分析代碼難度比較大我們直接分析attachCategories 這個方法
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count; // i 是分類的個數(shù)
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[I];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);//我看可以看到因為i--所以所以取得是最后一個分類的方法類表 根據(jù)isMeta判斷取得是 實例方法還是對象方法
if (mlist) {
mlists[mcount++] = mlist;// 我們?nèi)〉降姆椒惐?放到mlists這個二維數(shù)組中
fromBundle |= entry.hi->isBundle();
}
//下面屬性 和協(xié)議和上面類似
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data(); //這里 auto tw 就是 class_rw_t *rw 我們知道cla->data返回的就是這個結(jié)構(gòu)體指針 上面分析objc_class的源碼中可以看到
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount); //我們重點分析一下這個方法
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
//這是關(guān)鍵代碼上下文略去了
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) { //hasArray()這個方法表示自己是否有元素也就是 rw->methods是否為空 (如果一個類本身沒有任何實例方法,和有自己的實例方法 在合并分類里面的實例方法根據(jù)這個條件分情況考慮)
// many lists -> many lists
uint32_t oldCount = array()->count; //數(shù)組原來的長度
uint32_t newCount = oldCount + addedCount; //加上所有分類后的長度
setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); 分配新的空間
array()->count = newCount;
// 從array()->lists指向的那塊內(nèi)存開始拷貝 拷貝長度為oldCount 拷貝到array()->lists + addedCount起始的地址 (array()->lists的內(nèi)容移動了 但是array()->lists指向還沒變)
memmove(array()->lists + addedCount,
array()->lists,
oldCount * sizeof(array()->lists[0]));
// 從addedLists指向的那塊內(nèi)存開始拷貝 拷貝長度為addedCount 拷貝到array()->lists,t起始的地址
memcpy(array()->lists,
addedLists,
addedCount * sizeof(array()->lists[0]));
//上面的兩個方法其實是 吧我們分類的方法插到到了類方法前面
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
這個方法是吧一個類的所有分類里面的,方法,協(xié)議,屬性,存到類里面
method_list_t **mlists = (method_list_t *)malloc(cats->count * sizeof(mlists));
property_list_t **proplists = (property_list_t *)malloc(cats->count * sizeof(proplists));
protocol_list_t **protolists = (protocol_list_t *)malloc(cats->count * sizeof(protolists));
首先根據(jù)分類的個數(shù)通過malloc分配三個 二維數(shù)組. 我們通過分析method_list_t * * mlists存到東西來分析一下為什么是二維數(shù)組
[
[數(shù)組1里面存的是ClassA_ClassA_category_test1的所有對象方法],
[數(shù)組2里面存的是ClassA_ClassA_category_test2的所有對象方法],
[數(shù)組3里面存的是ClassA_ClassA_category_test2的所有對象方法]
]
一個類可以有 很多很多分類 一個分類可以有 很多對象方法
我們重點分析一下 while看上面代碼注釋
經(jīng)過上面的分析我們 可以得出下面的結(jié)論
分類其實就是一個結(jié)構(gòu)體 包含了,方法列表,屬性列表,協(xié)議列表
進過runtime會吧分類里面的信息插入到類的前面
介紹你知道的關(guān)于所有l(wèi)oad方法所有的知識點
(1)load方法的調(diào)用時機
(2)load調(diào)用方法的調(diào)用順序
load方法的調(diào)用時機是在runtime初始化的時候調(diào)用的,在main函數(shù)前面,我們分析load方法還是從源碼分析入手
//runtime初始化入口
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
//load_images是作為方法指針 我們看一下load_images方法
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
這里面有兩個要看的方法 1是prepare_load_methods 2是call_load_methods
//這個方法做了兩件事情 1是安排所有類 2便利所有分類生成一個結(jié)構(gòu)體數(shù)組
void prepare_load_methods(const headerType *mhdr)
{
size_t count, I;
runtimeLock.assertWriting();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[I];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
我們再看 schedule_class_load 和 add_category_to_loadable_list怎么吧
重點來了
我們看到 schedule_class_load 會遞歸的找一個類的父類,
調(diào)用add_class_to_loadable_list,如果在這個方法中會判斷類中是否實現(xiàn)了+load方法如果實現(xiàn)了就會吧 這個類和+load方法作為成員變量存在一個結(jié)構(gòu)體數(shù)組中. 這樣就生成了一個存著 類,和這個類對應(yīng)的load方法的結(jié)構(gòu)體數(shù)組 loadable_classes
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
//add_category_to_loadable_list和上面差不多 最后會有一個loadable_categories一個結(jié)構(gòu)體數(shù)組
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
//下面是結(jié)構(gòu)體的結(jié)構(gòu)
struct loadable_class {
Class cls; // may be nil
IMP method;
};
struct loadable_category {
Category cat; // may be nil
IMP method;
};
總結(jié):經(jīng)過上面的源碼分析我們發(fā)現(xiàn) runtime初始化期間會 生成一個loadable_class數(shù)組結(jié)構(gòu)體并且是按照加載類的順序,并且父類在前面子類在后的順序 和loadable_category數(shù)組結(jié)構(gòu)體順序和加載分類的順序相同
下面我們在分析剩下的部分直到找到+load是怎么自動調(diào)動的揭開+load神秘的面紗 我們接著分析call_load_methods方法
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
loadable_classes_used 這個值就是loadable_class數(shù)組的長度
從方法名字可以看出
call_class_loads //調(diào)用類的load方法
call_category_loads //調(diào)用分類的load方法
我們接下來分析一下這兩個方法
static void call_class_loads(void)
{
int I;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used; //loadable_classes數(shù)組的長度
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0; //賦值成0為了結(jié)束while (loadable_classes_used > 0) {...}循環(huán)
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
//我們還記得loadable_class結(jié)構(gòu)體的結(jié)構(gòu)吧 里面有+load方法的實現(xiàn) 有類
Class cls = classes[i].cls; //取出結(jié)構(gòu)體里面的類
load_method_t load_method = (load_method_t)classes[i].method; //取出結(jié)構(gòu)體里面的load方法指針
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, SEL_load); //用load函數(shù)指針直接調(diào)用.沒有走sendMessage()這一套流程
}
// Destroy the detached list.
if (classes) free(classes);
}
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
...
}
//我們看出調(diào)用分類的load方法 和調(diào)用類里面的load方法差不都 也是便利拿出 loadable_categorie 結(jié)構(gòu)體再取出 分類通過分類取出類, 再拿出load方法直接通過 結(jié)構(gòu)體中存的load函數(shù)指針調(diào)用load方法.
到這里load的源碼部分基本分析完了,其實從頭開始看也不難,我現(xiàn)在總結(jié)一下
總結(jié)
1如果有類實現(xiàn)了+load方法 先加載類的+load方法不管編譯順序誰前誰后
2加載類的+load方法和類的加載順序有關(guān)系 誰在前面先調(diào)用誰但是在調(diào)用類的+load方法之前會遞歸的判斷父類是否實現(xiàn)了+load方法父類實現(xiàn)了就先調(diào)用父類的+load方法
3分類的+load和分類的加載順序一樣
4調(diào)用+load的過程是在runtime初始化的時候會先便利所有類便利順序和類的加載順序一樣 判斷類是否實現(xiàn)了+load方法如果yes就用遞歸去便利父類.這樣吧所有實現(xiàn)load方法的 類和load函數(shù)指針存在結(jié)構(gòu)體中 分類同理 在調(diào)用的+load方法時再便利結(jié)構(gòu)體數(shù)組通過+load函數(shù)指針直接調(diào)用 分類同理
initialize
1調(diào)用時機
2調(diào)用過程
+initialize方法會在類第一次接收到消息時調(diào)用 所以分析一下類接受消息的的一個過程 關(guān)鍵代碼在 查找類里面消息這一步
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.read();
if (!cls->isRealized()) {
// Drop the read-lock and acquire the write-lock.
// realizeClass() checks isRealized() again to prevent
// a race while the lock is down.
runtimeLock.unlockRead();
runtimeLock.write();
realizeClass(cls);
runtimeLock.unlockWrite();
runtimeLock.read();
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
retry:
runtimeLock.assertReading();
// Try this class's cache.
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
return imp;
}
這里面有個_class_initialize
_class_initialize
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
// bootstrapping problem of this versus CF's call to
// objc_exception_set_functions().
#if __OBJC2__
@try
#endif
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
這個方法的關(guān)鍵代碼是 _class_initialize(supercls); 和 if(!cls->isInitialized() && !cls->isInitializing()) {...},callInitialize(cls);
在初始化子類的時候 先遞初始化父類, 條件標識表示類沒有初始化并且不是正在初始化, callInitialize(cls)表示調(diào)用+initialize方法
callInitialize
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
我們可以看到+initialize是通過objc_msgSend機制調(diào)用的.
總結(jié) 先調(diào)用父類的+initialize,再調(diào)用子類的+initialize
(先初始化父類,再初始化子類,每個類只會初始化1次)
+initialize和+load的很大區(qū)別是,+initialize是通過objc_msgSend進行調(diào)用的,所以有以下特點
如果子類沒有實現(xiàn)+initialize,會調(diào)用父類的+initialize(所以父類的+initialize可能會被調(diào)用多次)
如果分類實現(xiàn)了+initialize,就覆蓋類本身的+initialize調(diào)用
給分類動態(tài)綁定屬性底層原理
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
static AssociationsHashMap *_map;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
我么從源碼中分析AssociationsManager單利中有一個AssociationsHashMap, AssociationsHashMap的key就是(disguised_ptr_t disguised_object = DISGUISE(object)是object. value是ObjectAssociationMap, ObjectAssociationMap的key是void*就是傳過來的void *key, value是ObjcAssociation

給分類動態(tài)綁定week屬性怎么搞
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
OBJC_ASSOCIATION_ASSIGN 對應(yīng)屬性的assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC 對應(yīng)屬性的nonatomic strong
OBJC_ASSOCIATION_COPY_NONATOMIC 對應(yīng)屬性的nonatomic copy
OBJC_ASSOCIATION_RETAIN 對應(yīng)屬性的atomic strong
OBJC_ASSOCIATION_COPY 對應(yīng)屬性的atomic copy
我們發(fā)現(xiàn)對應(yīng)的屬性沒有week 我們怎么做呢? 我們可以用OBJC_ASSOCIATION_ASSIGN代替week但是assign和week我們知道區(qū)別assign修飾對象當對象釋放后不會自動變成nil 我們需要注意的是在對象釋放后手動吧對象變成nil.這樣是有風(fēng)險的我們可能會忘記自己當對象釋放的時候吧對象變成nil.
我們可以設(shè)計一個NSObject分類增加一個帶回調(diào)的方法.就是當對象將要釋放時候,回調(diào)block. 我們在block中把對象手動賦值nil即可
#import "UIView+weak.h"
#import <objc/runtime.h>
#import <CYLDeallocBlockExecutor/CYLDeallocBlockExecutor.h>
@implementation UIView (weak)
- (NSObject *)obj{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setObj:(NSObject *)obj{
objc_setAssociatedObject(self, @selector(obj), obj, OBJC_ASSOCIATION_ASSIGN);
[obj cyl_willDeallocWithSelfCallback:^(__unsafe_unretained id owner, NSUInteger identifier) {
self.obj = nil;
}];
//這樣是錯的? 因為屬性雖然是 weak 但是運行時objc_setAssociatedObject這個方法沒有weak 只有OBJC_ASSOCIATION_ASSIGN OBJC_ASSOCIATION_ASSIGN
}
@end
//
// NSObject+CYLDeallocBlockExecutor.m
// CYLDeallocBlockExecutor
// v1.2.0
// Created by 微博@iOS程序犭袁 ( http://weibo.com/luohanchenyilong/ ) on 15/12/27.
// Copyright ? 2015年 https://github.com/ChenYilong . All rights reserved.
//
#import "NSObject+CYLDeallocBlockExecutor.h"
#import <objc/runtime.h>
#include <libkern/OSAtomic.h>
#import <pthread.h>
static const char CYLDeallocCallbackModelKey;
static dispatch_queue_t _deallocCallbackQueue = 0;
const NSUInteger CYLDeallocCallbackIllegalIdentifier = 0;
@interface CYLDeallocCallbackModel : NSObject
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, strong) NSMutableDictionary *callbacksDictionary;
@property (nonatomic, unsafe_unretained) id owner;
@end
@implementation CYLDeallocCallbackModel
- (instancetype)initWithOwner:(id)owner {
self = [super init];
if (self) {
_owner = owner;
pthread_mutex_init(&_lock, NULL);
_callbacksDictionary = [NSMutableDictionary new];
}
return self;
}
- (NSUInteger)addSelfCallback:(CYLDeallocSelfCallback)selfCallback {
static volatile int64_t globalIdentifier = 0;
if (!selfCallback) {
return CYLDeallocCallbackIllegalIdentifier;
}
// globalIdentifier 運行一次就自增1
NSUInteger newIdentifier = (NSUInteger)OSAtomicIncrement64(&globalIdentifier);
NSNumber *newIdentifierNumber = @(newIdentifier);
if (newIdentifierNumber) {
pthread_mutex_lock(&_lock);
[_callbacksDictionary setObject:[selfCallback copy] forKey:newIdentifierNumber];
pthread_mutex_unlock(&_lock);
return newIdentifier;
}
return CYLDeallocCallbackIllegalIdentifier;
}
- (BOOL)removeCallbackWithIdentifier:(NSUInteger)identifier {
if (identifier == CYLDeallocCallbackIllegalIdentifier) {
return NO;
}
NSNumber *identifierNumber = [NSNumber numberWithUnsignedInteger:identifier];
if (identifierNumber) {
pthread_mutex_lock(&_lock);
[_callbacksDictionary removeObjectForKey:identifierNumber];
pthread_mutex_unlock(&_lock);
return YES;
}
return NO;
}
- (void)removeAllCallbacks {
pthread_mutex_lock(&_lock);
[_callbacksDictionary removeAllObjects];
pthread_mutex_unlock(&_lock);
}
- (void)dealloc {
[_callbacksDictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *identifier, CYLDeallocSelfCallback block, BOOL * _Nonnull stop) {
block(_owner, identifier.unsignedIntegerValue);
}];
pthread_mutex_destroy(&_lock);
}
@end
const void *CYLDeallocExecutorsKey = &CYLDeallocExecutorsKey;
@implementation NSObject (CYLDeallocBlockExecutor)
- (NSHashTable *)cyl_deallocExecutors {
NSHashTable *table = objc_getAssociatedObject(self,CYLDeallocExecutorsKey);
if (!table) {
table = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory];
objc_setAssociatedObject(self, CYLDeallocExecutorsKey, table, OBJC_ASSOCIATION_RETAIN);
}
return table;
}
- (void)cyl_executeAtDealloc:(CYLDeallocExecutorBlock)block {
if (block) {
CYLDeallocExecutor *executor = [[CYLDeallocExecutor alloc] initWithBlock:block];
dispatch_sync(self.deallocCallbackQueue, ^{
[[self cyl_deallocExecutors] addObject:executor];
});
}
}
- (dispatch_queue_t)deallocCallbackQueue {
if (_deallocCallbackQueue == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_deallocCallbackQueue = dispatch_queue_create("com.chenyilong.CYLDeallocBlockExecutor.deallocCallbackQueue", DISPATCH_QUEUE_SERIAL);
});
}
return _deallocCallbackQueue;
}
- (NSUInteger)cyl_willDeallocWithSelfCallback:(CYLDeallocSelfCallback)selfCallback {
if (!selfCallback) {
return CYLDeallocCallbackIllegalIdentifier;
}
__block CYLDeallocCallbackModel *model = nil;
dispatch_sync(self.deallocCallbackQueue, ^{
model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
if (!model) {
model = [[CYLDeallocCallbackModel alloc] initWithOwner:self];
objc_setAssociatedObject(self, &CYLDeallocCallbackModelKey, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
});
NSUInteger newIdentifier = [model addSelfCallback:selfCallback];
return newIdentifier;
}
- (BOOL)cyl_cancelDeallocCallbackWithIdentifier:(NSUInteger)identifier {
CYLDeallocCallbackModel *model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
if (model) {
return [model removeCallbackWithIdentifier:identifier];
}
return NO;
}
- (void)cyl_cancelAllDeallocCallbacks {
CYLDeallocCallbackModel *model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
if (model) {
[model removeAllCallbacks];
}
}
@end
上面的代碼來自 https://github.com/ChenYilong
__weak的底層實現(xiàn)
__weak的使用場景這里就不多說了這主要 分析一下__weak修飾的修飾的對象在釋放后自動變成nil的這個過程
看下面代碼吧
- (void)dealloc {
_objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && //標識nonpointer 是否是優(yōu)化過的類
!isa.weakly_referenced && //是否有被弱引用指向過
!isa.has_assoc && // 是否有設(shè)置過關(guān)聯(lián)對象
!isa.has_cxx_dtor && // 是否有C++的析構(gòu)函數(shù)
!isa.has_sidetable_rc)) // 是否引用計數(shù)器是否過大無法存儲在isa中
{
assert(!sidetable_present());
free(this);
}
else {
//如果有弱引用對象指向過 走這里
object_dispose((id)this);
}
}
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return 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); //如果有c++的析構(gòu)函數(shù)就去執(zhí)行析構(gòu)函數(shù)
if (assoc) _object_remove_assocations(obj); //如果綁定過對象 就去解除綁定
obj->clearDeallocating();
}
return obj;
}
inline void objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa. 不是優(yōu)化過的類
sidetable_clearDeallocating();
}
else if (slowpath(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());
}
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
//我們看 關(guān)鍵代碼
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
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);
}
我們可以這樣理解
SideTable& table = SideTables()[this]; //一個全局字典
以對象(self)為key 里面 value 也是一個字典 table(weak_table)
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
weak_table(字典)根據(jù)key(referent) 取出entry entry-> referrers相當于數(shù)組里面存的是 指向?qū)ο?self)的__weak指針
便利這個數(shù)組把所有__weal指針手動賦值為nil.
以上就是 __weak指針當對象釋放的時候 自動會清空的大致過程.