这里会把类相关、程序启动类信息填充、引用计数都会讲下。想要深入了解OC
的动态性,就必须去研究runtime
的代码,所幸它是开源的,你可以在源码下载不同的版本。
typedef struct objc_class *Class;
typedef struct objc_object *id;
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();
}
....省略
}
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
....省略
}
根据上面的声明,凡是首地址是isa
,都可以被认为是objc
中的对象。运行时可以通过isa
获取到该对象是属于什么类(Class
)。那类对象有所属的类吗?有,称之为元类(Metaclass
)。
上面这张图可以把isa
和superclass
表现的很清楚。
结构体 isa_t
接下来看下isa
的结构:
#if (!__LP64__ || TARGET_OS_WIN32 || TARGET_OS_SIMULATOR)
# define SUPPORT_PACKED_ISA 0
#else
# define SUPPORT_PACKED_ISA 1
#endif
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
...省略
#if SUPPORT_INDEXED_ISA
...
isa
指针是一个联合体,在不同环境下,结构体中属性值稍有差别。
-
SUPPORT_INDEXED_ISA
:通过一些资料查询,它是用于手表的; -
nonpointer
:它为1时,表示使用了优化的isa
指针,包含了引用计数,析构状态等; -
has_assoc
: 是否包含关联对象,如果没有,会更快的释放内存; -
has_cxx_dtor
:是否包含析构函数,如果没有,会更快的释放内存; -
shiftcls
:类的指针; -
magic
:用于判断是否完成初始化; -
weakly_referenced
:对象是否指向一个弱引用对象,没有弱引用对象可以更快的被释放; -
deallocating
:对象是否正在销毁; -
has_sidetable_rc
:对象的引用计数太大了,存不下; -
extra_rc
:引用计数,但是比retaincount
小1;
用一张霜神的图片,可以更容易看清楚:
isa_内存布局
在x86_64
架构上,可以看到类的指针用了47位来表示,因为类的指针要按照8字节对齐,所以后3位都是0,而为了减小内存的消耗,那3位可以表示为别的值,所以类指针真正有效的就只有44位。
inline Class objc_object::ISA()
{
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
通过isa.bits & ISA_MASK
进行位运算,只取其中[3 - 47]
,然后返回Class
。
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
if (!nonpointer) {
isa.cls = cls;
} else {
isa_t newisa(0);
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
}
}
根据上面的图片,把ISA_MAGIC_VALUE
的值放入64位空间里面,可以知道是给nonpointer
和magic
进行了赋值,has_cxx_dtor
、shiftcls
一样的操作。
cache_t
cache_t cache
从字面的是缓存的意思,肯定是优化性能的,先看下它的结构:
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
}
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
}
-
_buckets
是一个散列表,用来存储方法缓存。bucket_t
是一个key
和IMP
的结构体。 -
_mask
分配bucket
的总数。 -
_occupied
表示目前占用缓存bucket
的个数。
Cache的作用主要是优化方法调用的性能,当对象收到消息后,会优先去Cache中查找,如果Cache中没有才会去类的方法列表找。如果没有Cache,我们每次调用方法都去类的方法找,会严重影响性能。
class_data_bits_t
一个类的方法,属性和遵循的协议都在class_data_bits_t
里面。我们先看下代码:
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
}
data()
方法来返回class_rw_t *
指针,里面存储的是类的信息,它跟objc_object::ISA()
方法是一样的。
class_rw_t和class_ro_t
通过名字都能看出来class_rw_t
是可读写的,class_ro_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;
}
其中有一个指向常量的指针ro
,它存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
这个结构跟通过clang -rewrite-objc
之后产出的类结构完全一样:
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
这样的类结构跟这里的类结构还有一点不一样就是class_rw_t
,那它是怎么调整的呢?什么时候操作呢?还有运行期类的信息什么时候赋值呢?
map_images_nolock
通过调式,发现Runtime
的入口是 objc-os.mm
里面的函数 _objc_init
。在这个函数里面,动态库注册了3个函数回调map_2_images
、load_images
、unmap_image
。
void _objc_init(void)
└──const char *map_2_images(...)
└──const char *map_images_nolock(...)
└──void _read_images(header_info **hList, uint32_t hCount)
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
if (firstTime) {
preopt_init();
}
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[I];
// 将mhdr通过appendHeader函数添加到链表中 由FirstHeader,LastHeader和HeaderCount维护
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
// Size some data structures based on main executable's size
#if __OBJC2__
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
//Fix up old objc_msgSend_fixup call sites 基本上都为0
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
}
hList[hCount++] = hi;
}
}
if (firstTime) {
//将被引用的sel数量存到静态变量SelrefCount中;
//注册一些sel到namedSelectors中
sel_init(selrefCount);
//初始化AutoreleasePoolPage用于autorelease机制
// 初始化SideTable用于__weak变量引用管理
arr_init();
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
这个函数主要就是通过mhdrs
生成hList
,然后传递给_read_images
函数。
_read_images
这个方法是处理类的相关信息的,有些长,一点一点来分析。
类信息
for (EACH_HEADER) {
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[I];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
...
}
}
bool mustReadClasses(header_info *hi)
{
const char *reason;
// If the image is not preoptimized then we must read classes.
if (!hi->isPreoptimized()) {
reason = nil; // Don't log this one because it is noisy.
goto readthem;
}
assert(!hi->isBundle()); // no MH_BUNDLE in shared cache
// If the image may have missing weak superclasses then we must read classes
if (!noMissingWeakSuperclasses()) {
reason = "the image may contain classes with missing weak superclasses";
goto readthem;
}
// If there are unresolved future classes then we must read classes.
if (haveFutureNamedClasses()) {
reason = "there are unresolved future classes pending";
goto readthem;
}
// readClass() does not need to do anything.
return NO;
readthem:
return YES;
}
bool header_info::isPreoptimized() const
{
// preoptimization disabled for some reason
if (!preoptimized) return NO;
// image not from shared cache, or not fixed inside shared cache
if (!info()->optimizedByDyld()) return NO;
return YES;
}
在map_images_nolock
函数中调用了preopt_init();
,通过调式,它会把preoptimized
置为NO
,所以isPreoptimized
函数也是一直返回NO
的,因此mustReadClasses
会返回YES
。
_getObjc2ClassList
函数会调用getsectiondata
来返回所有的类列表,这是读取二进制文件也就是mach-o
得到的,具体的之前专门文章介绍。
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
...省略
addNamedClass(cls, mangledName, replacing);
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertWriting();
Class old;
if ((old = getClass(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getNonMetaClass uses name lookups. Classes not found by name
// lookup must be in the secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
}
readClass
函数主要就是调用addNamedClass
函数,通过NXMapInsert
把类插入进去,把类跟类名做一个关联。
方法信息
static size_t UnfixedSelectors;
sel_lock();
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
sel_unlock();
这个就比较简单了,就是把方法名插入到namedSelectors
表里面。
协议信息
struct protocol_t : objc_object {
const char *mangledName;
...
}
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
static void readProtocol(protocol_t *newproto, Class protocol_class,
NXMapTable *protocol_map,
bool headerIsPreoptimized, bool headerIsBundle)
{
auto insertFn = headerIsBundle ? NXMapKeyCopyingInsert : NXMapInsert;
...
newproto->initIsa(protocol_class); // fixme pinned
insertFn(protocol_map, newproto->mangledName, newproto);
...
}
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[I]);
}
}
static void remapProtocolRef(protocol_t **protoref)
{
runtimeLock.assertLocked();
protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
if (*protoref != newproto) {
*protoref = newproto;
UnfixedProtocolReferences++;
}
}
通过查看protocol_t
结构,发现它本质上也是一个对象,它的isa
就是(Class)&OBJC_CLASS_$_Protocol
,然后把协议都插入到protocol_map
表里。
通过*protoref
的名字,如果能在protocol_map
找到,而且地址不一样,就进行替换。
non-lazy classes
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
realizeClass(cls);
}
}
_getObjc2NonlazyClassList
是获取有重写+load
方法的类,对应的是_OBJC_LABEL_NONLAZY_CLASS_$[]
。这个方法的重点是realizeClass
。
realizeClass
static Class realizeClass(Class cls)
{
runtimeLock.assertWriting();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));
...省略
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls);
return cls;
}
ro = (const class_ro_t *)cls->data();
,从这行代码可以看出从二进制读取到的class_ro_t
结构,而不是class_rw_t
。
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
// class_t->data is class_rw_t, not class_ro_t
#define RW_REALIZED (1<<31)
// class has started realizing but not yet completed it
#define RW_REALIZING (1<<19)
这几行代码就是给rw
结构进行赋值,并对flag
进行改变,表示开始realizing
,但还没完成。
if (!cls) return nil;
if (cls->isRealized()) return cls;
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));
通过这4行代码,递归对类进行realize
,从这也能看出类的superclass
和isa
结构,跟这个图进行认证。
cls->initClassIsa(metacls);
这行代码可以说明类的isa
就是metacls
。
methodizeClass
这个方法主要就是对rw
的方法、协议、属性进行赋值。这里有一点比较奇怪的是为啥就仅仅对重写+load
方法的类进行realizeClass
,而不是所有的类?我就全局搜索都有那些地方调用这个方法,发现有好多地方会调用,如lookUpImpOrForward
、look_up_class
等,他们都会进行判断,如果类没有isRealized
,就会调用这个方法。这样做的目的可能是为了性能考虑,让启程尽快启动。
分类
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[I];
Class cls = remapClass(cat->cls);
...
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
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);
};
addUnattachedCategoryForClass
方法把分类的相关信息存到category_map
表里面。接下来如果类已经isRealized
过了,就调用remethodizeClass
方法,它首先在分类表里根据cls
把相关信息删除,再调用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;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[I];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
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();
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);
}
这个方法比较简单,就是从cats
获取所有的分类信息,然后对rw
进行赋值。
这里面第一个回调函数map_images
就没了,接下来是load_images
函数,它主要是操作+load
方法。
load_images
void 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();
}
通过prepare_load_methods
函数找到所有重写+load
方法的类和分类,然后通过call_load_methods
调用他们。
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);
}
}
_getObjc2NonlazyClassList
前面已经讲过,_getObjc2NonlazyCategoryList
它获取的是重写+load
的所有分类,它对应的是_OBJC_LABEL_NONLAZY_CATEGORY_$[]
。
// class +load has been called
#define RW_LOADED (1<<23)
static void schedule_class_load(Class cls)
{
if (!cls) return;
if (cls->data()->flags & RW_LOADED) return;
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
static struct loadable_category *loadable_categories = nil;
static int loadable_categories_used = 0;
static int loadable_categories_allocated = 0;
struct loadable_category {
Category cat; // may be nil
IMP method;
};
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 (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++;
}
IMP objc_class::getLoadMethod()
{
runtimeLock.assertLocked();
const method_list_t *mlist;
mlist = ISA()->data()->ro->baseMethods();
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
if (0 == strcmp(name, "load")) {
return meth.imp;
}
}
}
return nil;
}
schedule_class_load(cls->superclass);
这行代码递归调用直至到NSObject
,因为NSObject
的父类为nil
,然后通过add_class_to_loadable_list
方法把有+load
方法加入到loadable_classes
,最后把当前类的flag
置为+load
已经调用的状态。从这也可以看到是先从父类的+load
开始算起的。
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 (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++;
}
分类的+load
存取是直接从分类的方法找到有+load
的,然后放到loadable_categories
。
call_load_methods
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
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;
}
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 = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
(*load_method)(cls, SEL_load);
}
if (classes) free(classes);
}
调用+load
方法也是先调用类的,再调用分类的,类的调用就是从loadable_classes
获取所有的类的+load
实现,然后直接调用。
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
这段注释也能很好的诠释这些。
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()) {
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
// Compact detached list (order-preserving)
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[I];
} else {
shift++;
}
}
used -= shift;
// Copy any new +load candidates from the new list to the detached list.
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[I];
}
// Destroy the new list.
if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list.
// But if there's nothing left to load, destroy the list.
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
return new_categories_added;
}
这个方法我是有点疑惑的,不太明白又加层机制是为什么?首先cls->isLoadable()
是一直返回yes
的,cls
不存在的可能性我认为不存在(这点有待探讨)。Re-entrant
可重入问题,在call_load_methods
里面已经解决了。
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
至于在+load
方法里面动态生成一个分类,那为啥类的时候不考虑那种情况呢,再说loadable_categories
是在prepare_load_methods
方法里面确定好的,没有那个地方再对它进行修改。综合几种情况下来,还是不太明白为啥那样做的原因。
引用计数
这里简单的说下引用计数的原理,引用计数跟objc_storeStrong
这个函数紧密相连,引用计数的增加减少都是通过它,这个函数是编译阶段由编译器在合适的位置自己增加的,所以才叫自动引用计数。还有一种情况是直接调用objc_retain
增加引用计数的,但是减少的话都是通过objc_storeStrong
。(这段话可能不太准确,但是我测试好几种情况都是这样的,但是不管还有那种情况,最后肯定都是调用retain
、release
来进行增加减少引用计数的)
void objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj);
*location = obj;
objc_release(prev);
}
下面看2个例子比较容易明白,其中第2个例子就是直接调用objc_retain
来增加引用计数的。
#1:
{
NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = [[NSObject alloc]init];
obj2 = obj1;
}
#2:
{
NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = obj1;
}
当出括号时,会调用2次objc_storeStrong
,当调用第2个时,会调用dealloc
。
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain();
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(tryRetain && newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (!tryRetain && sideTableLocked) sidetable_unlock();
return nil;
}
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (!handleOverflow) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
}
最主要的是newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
这行代码,它是增加引用计数的,上面讲述isa
时,知道最后的8位是存引用计数的,如果引用计数太大,8位不够存时(超过255),就用sideTable
表结构,这一块就不讲了,因为感觉用不到,255已经够大了。
static ALWAYS_INLINE uintptr_t
addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
{
return __builtin_addcl(lhs, rhs, carryin, carryout);
}
static ALWAYS_INLINE uintptr_t
subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
{
return __builtin_subcl(lhs, rhs, carryin, carryout);
}
这2个函数是LLVM
提供的,就是简单的数相加,不过如果超过最大值或者最小值时carryout
就不为0了。从这也能看出来为啥实际的引用计数比extra_rc
加1的原因,alloc
的时候是不增加引用计数的。
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// Really deallocate.
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
减少引用计数后,if (slowpath(carry)) goto underflow;}
,直接就开始((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
。
runtime
相关就讲到这里,如果有什么错误,请相互交流指正。
网友评论