对象的本质
NSObject * obj = [[NSObject alloc] init];
转化成c++可以到本质是一个结构体
struct NSObject_IMPL{
Class isa.
};
typedef struct objc_class * Class;
可以看出isa 是指向objc_class结构的指针
obj对象的地址,就是结构体的地址,结构体里面第一个元素就是isa,结构体的地址也就是isa指针的地址.
总结对象的本质就是一个指向类结构体的指针 我们NSLog(@"%@", obj)其实是打印了一个 指向NSObject这个类的指针的地址.
下面是一个Student转成的c++
struct Student_IMPL{
struct NSObject_IMPL NSObject_IVARS;
int _num
int _age
};
image.png
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结构体中isa实例变量指向的地址
(Class) $14 = 0x0000000105c37a48 LCKVCtest
(lldb) p/x 0x0000000105c37a48 & 0x0000000ffffffff8ULL // 对象中的isa& ISA_MASK得到类的地址
(unsigned long long) $15 = 0x0000000105c37a48
(lldb) p/x &(kvc->isa) 得到结构体isa的地址(isa是结构体中第一个成员变量所以也是结构体的地址也就是对象的地址)
(__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对象转正我们自定的结构体指针类对象中isa& ISA_MASK指向元类对象 元类对象的中的isa& ISA_MASK指向NSObejc元类,NSObejc元类中的isa指向自己. (不用& ISA_MASK)
描述对象类和元类的关系这里来个张神图镇场再合适不过了
image.png
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;
这里是类对象的内存布局 但是遗憾的是!__OBJC2__ 已经不是现在的版本了.
现在的版本是__OBJC2__
并且OBJC2_UNAVAILABLE 说明在__OBJC2__版本下这个结构体也是不可用的,我们可以简单分析一下
isa 可以简单的人为是指向元类(其实需要$ISA_MASK)
super_class 指向父类
ivars 是成员变量列表
objc_method_list 方法列表
cache 方法缓存
protocols 协议类表
现在的class 结构如下
//对象结构体
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 结构体
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));
};
以上是现在objc_class现在的内存布局 我们摘出存在objc_class结构体内存里面的关键信息
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架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息
has_assoc
是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
存储着Class、Meta-Class对象的内存地址信息
magic
用于在调试时分辨对象是否未完成初始化
weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快
deallocating
对象是否正在释放
extra_rc
里面存储的值是引用计数器减1
has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
分类底层的实现
分类在runtime中结构是这样的我们每一个分类 都会生成一个这样的结构体
下面是分类在objc代码源码中的结构体
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 看看分类转换成的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
};
//关于协议在分类中
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;
};
我们从上面可以看到一个类的一个分类底层其实是一个结构体.
结构体中包含的有
1类的 名字
2..
3实例方法列表
4对象方法类表
5协议类表
6属性列表
我们知道一个对象调用一个实例方法寻找这个方法其实是在类里面的寻找的,如果我们调用一个分类的实例方法,是怎么找到这个方法的呢?
我们根据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 是分类的个数
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[I];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);//我看可以看到因为i--所以所以取得是最后一个分类的方法类表 根据isMeta判断取得是 实例方法还是对象方法
if (mlist) {
mlists[mcount++] = mlist;// 我们取到的方法类表 放到mlists这个二维数组中
fromBundle |= entry.hi->isBundle();
}
//下面属性 和协议和上面类似
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返回的就是这个结构体指针 上面分析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);
}
//这是关键代码上下文略去了
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) { //hasArray()这个方法表示自己是否有元素也就是 rw->methods是否为空 (如果一个类本身没有任何实例方法,和有自己的实例方法 在合并分类里面的实例方法根据这个条件分情况考虑)
// many lists -> many lists
uint32_t oldCount = array()->count; //数组原来的长度
uint32_t newCount = oldCount + addedCount; //加上所有分类后的长度
setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); 分配新的空间
array()->count = newCount;
// 从array()->lists指向的那块内存开始拷贝 拷贝长度为oldCount 拷贝到array()->lists + addedCount起始的地址 (array()->lists的内容移动了 但是array()->lists指向还没变)
memmove(array()->lists + addedCount,
array()->lists,
oldCount * sizeof(array()->lists[0]));
// 从addedLists指向的那块内存开始拷贝 拷贝长度为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]));
}
}
这个方法是吧一个类的所有分类里面的,方法,协议,属性,存到类里面
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));
首先根据分类的个数通过malloc分配三个 二维数组. 我们通过分析method_list_t * * mlists存到东西来分析一下为什么是二维数组
[
[数组1里面存的是ClassA_ClassA_category_test1的所有对象方法],
[数组2里面存的是ClassA_ClassA_category_test2的所有对象方法],
[数组3里面存的是ClassA_ClassA_category_test2的所有对象方法]
]
一个类可以有 很多很多分类 一个分类可以有 很多对象方法
我们重点分析一下 while看上面代码注释
经过上面的分析我们 可以得出下面的结论
分类其实就是一个结构体 包含了,方法列表,属性列表,协议列表
进过runtime会吧分类里面的信息插入到类的前面
介绍你知道的关于所有load方法所有的知识点
(1)load方法的调用时机
(2)load调用方法的调用顺序
load方法的调用时机是在runtime初始化的时候调用的,在main函数前面,我们分析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便利所有分类生成一个结构体数组
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 会递归的找一个类的父类,
调用add_class_to_loadable_list,如果在这个方法中会判断类中是否实现了+load方法如果实现了就会吧 这个类和+load方法作为成员变量存在一个结构体数组中. 这样就生成了一个存着 类,和这个类对应的load方法的结构体数组 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一个结构体数组
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++;
}
//下面是结构体的结构
struct loadable_class {
Class cls; // may be nil
IMP method;
};
struct loadable_category {
Category cat; // may be nil
IMP method;
};
总结:经过上面的源码分析我们发现 runtime初始化期间会 生成一个loadable_class数组结构体并且是按照加载类的顺序,并且父类在前面子类在后的顺序 和loadable_category数组结构体顺序和加载分类的顺序相同
下面我们在分析剩下的部分直到找到+load是怎么自动调动的揭开+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数组的长度
从方法名字可以看出
call_class_loads //调用类的load方法
call_category_loads //调用分类的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数组的长度
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0; //赋值成0为了结束while (loadable_classes_used > 0) {...}循环
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
//我们还记得loadable_class结构体的结构吧 里面有+load方法的实现 有类
Class cls = classes[i].cls; //取出结构体里面的类
load_method_t load_method = (load_method_t)classes[i].method; //取出结构体里面的load方法指针
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, SEL_load); //用load函数指针直接调用.没有走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;
}
}
...
}
//我们看出调用分类的load方法 和调用类里面的load方法差不都 也是便利拿出 loadable_categorie 结构体再取出 分类通过分类取出类, 再拿出load方法直接通过 结构体中存的load函数指针调用load方法.
到这里load的源码部分基本分析完了,其实从头开始看也不难,我现在总结一下
总结
1如果有类实现了+load方法 先加载类的+load方法不管编译顺序谁前谁后
2加载类的+load方法和类的加载顺序有关系 谁在前面先调用谁但是在调用类的+load方法之前会递归的判断父类是否实现了+load方法父类实现了就先调用父类的+load方法
3分类的+load和分类的加载顺序一样
4调用+load的过程是在runtime初始化的时候会先便利所有类便利顺序和类的加载顺序一样 判断类是否实现了+load方法如果yes就用递归去便利父类.这样吧所有实现load方法的 类和load函数指针存在结构体中 分类同理 在调用的+load方法时再便利结构体数组通过+load函数指针直接调用 分类同理
initialize
1调用时机
2调用过程
+initialize方法会在类第一次接收到消息时调用 所以分析一下类接受消息的的一个过程 关键代码在 查找类里面消息这一步
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!");
}
}
这个方法的关键代码是 _class_initialize(supercls); 和 if(!cls->isInitialized() && !cls->isInitializing()) {...},callInitialize(cls);
在初始化子类的时候 先递初始化父类, 条件标识表示类没有初始化并且不是正在初始化, callInitialize(cls)表示调用+initialize方法
callInitialize
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
我们可以看到+initialize是通过objc_msgSend机制调用的.
总结 先调用父类的+initialize,再调用子类的+initialize
(先初始化父类,再初始化子类,每个类只会初始化1次)
+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点
如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
如果分类实现了+initialize,就覆盖类本身的+initialize调用
给分类动态绑定属性底层原理
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
image.png给分类动态绑定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 对应属性的assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC 对应属性的nonatomic strong
OBJC_ASSOCIATION_COPY_NONATOMIC 对应属性的nonatomic copy
OBJC_ASSOCIATION_RETAIN 对应属性的atomic strong
OBJC_ASSOCIATION_COPY 对应属性的atomic copy
我们发现对应的属性没有week 我们怎么做呢? 我们可以用OBJC_ASSOCIATION_ASSIGN代替week但是assign和week我们知道区别assign修饰对象当对象释放后不会自动变成nil 我们需要注意的是在对象释放后手动吧对象变成nil.这样是有风险的我们可能会忘记自己当对象释放的时候吧对象变成nil.
我们可以设计一个NSObject分类增加一个带回调的方法.就是当对象将要释放时候,回调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的底层实现
__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 是否是优化过的类
!isa.weakly_referenced && //是否有被弱引用指向过
!isa.has_assoc && // 是否有设置过关联对象
!isa.has_cxx_dtor && // 是否有C++的析构函数
!isa.has_sidetable_rc)) // 是否引用计数器是否过大无法存储在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++的析构函数就去执行析构函数
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. 不是优化过的类
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) {
//我们看 关键代码
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(字典)根据key(referent) 取出entry entry-> referrers相当于数组里面存的是 指向对象(self)的__weak指针
便利这个数组把所有__weal指针手动赋值为nil.
以上就是 __weak指针当对象释放的时候 自动会清空的大致过程.
网友评论