objc源码解析
objc的源码解析。
AutoreleasePoolPage
class AutoreleasePoolPage
{
#define POOL_SENTINEL nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
......
}
从上面的类成员变量可以看出
- autoreleasePoolPage的默认大小是PAGE_MAX_SIZE
- 每个线程都有自己的AutoreleasePoolPage
- AutoReleasePoolPage是通过双向链表连接起来的。
下面我们来看看autoreleasepoolpage的重要api
// 自动释放某个对象,即将该变量放入autoreleasePool
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
assert(!dest || *dest == obj);
return obj;
}
// 调用push的时候,都是创建一个新的Page,然后在Page中加入SENTINEL
// 一般在线程runloop开始的时候会调用push函数
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_SENTINEL);
} else {
dest = autoreleaseFast(POOL_SENTINEL);
}
assert(*dest == POOL_SENTINEL);
return dest;
}
// 调用pop的时候,token一般是POOL_SENTINEL
// 然后page会调用releaseUntil(stop)来释放push进来的全部对象,以POOL_SENTINEL为界限
// 线程一般在runloop的开始的时候会调用push,而runloop结束的时候调用pop
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
page = pageForPointer(token);
stop = (id *)token;
if (DebugPoolAllocation && *stop != POOL_SENTINEL) {
// This check is not valid with DebugPoolAllocation off
// after an autorelease with a pool page but no pool in place.
_objc_fatal("invalid or prematurely-freed autorelease pool %p; ",
token);
}
if (PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
}
else if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
在autoreleasePoolPage中有hotPage和coldPage的概念。刚创建的autoreleasePoolPage是hot的,其存放在线程的tls中,方便存取,而coldPage是最先创建的autoreleasePoolPage。例如上面的push函数,其创建的page都会被立马设置成hotpage。
void *
objc_autoreleasePoolPush(void)
{
if (UseGC) return nil;
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
if (UseGC) return;
AutoreleasePoolPage::pop(ctxt);
}
void *
_objc_autoreleasePoolPush(void)
{
return objc_autoreleasePoolPush();
}
void
_objc_autoreleasePoolPop(void *ctxt)
{
objc_autoreleasePoolPop(ctxt);
}
以上4个函数就是runloop在真正调用的函数。
SideTable
sideTable在Objective-c中负责存储引用计数和弱引用关系,这些功能可以从其结构中一窥端倪。
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
bool trylock() { return slock.trylock(); }
// Address-ordered lock discipline for a pair of side tables.
template<bool HaveOld, bool HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<bool HaveOld, bool HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
从上述代码可以看出,sideTable结构体主要包含三个变量
- slock:锁变量
- refcnts:引用计数
- weak_table:弱引用表
refcnts
OC的引用计数功能是由NonPointerISA和sideTable功能来实现的,我们已经知道,当retainCount比较小的时候,是通过isa来存储的,当retainCount变得很大,isa存不下的时候,就会用到sidetable来存放。这一点我们可以通过几个函数来看。
// Move the entire retain count to the side table,
// as well as isDeallocating and weaklyReferenced.
void
objc_object::sidetable_moveExtraRC_nolock(size_t extra_rc,
bool isDeallocating,
bool weaklyReferenced);
// Move some retain counts to the side table from the isa field.
// Returns true if the object is now pinned.
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc);
// Move some retain counts from the side table to the isa field.
// Returns the actual count subtracted, which may be less than the request.
size_t
objc_object::sidetable_subExtraRC_nolock(size_t delta_rc);
下面我们就看看sidetable是如何在retain的时候增加引用计数,在release的时候减少引用计数的
// sidetable retain
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.indexed);
#endif
SideTable& table = SideTables()[this];
if (table.trylock()) {
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE; // 引用计数加1
}
table.unlock();
return (id)this;
}
return sidetable_retain_slow(table);
}
// sidetable release
uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.indexed);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
if (table.trylock()) {
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
it->second -= SIDE_TABLE_RC_ONE; // 引用计数减1
}
table.unlock();
if (do_dealloc && performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
return sidetable_release_slow(table, performDealloc);
}
weak_table
// We cannot use a C++ static initializer to initialize SideTables because
// libc calls us before our C++ initializers run. We also don't want a global
// pointer to this struct because of the extra indirection.
// Do it the hard way.
alignas(StripedMap<SideTable>) static uint8_t
SideTableBuf[sizeof(StripedMap<SideTable>)];
static void SideTableInit() {
new (SideTableBuf) StripedMap<SideTable>();
}
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
从上述代码可以看出,SideTable的初始化是静态的,其是全局静态变量,这一点值得关注。下面我们结合代码看看,SideTable是如何存储和销毁weak变量的。
template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
......
// 如果在location对应的位置下有老的weak变量,在weak_table中反注册老变量
// Clean up old value, if any.
if (HaveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// 如果是新的weak变量,那么在weak_table中注册该变量
// Assign new value, if any.
if (HaveNew) {
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table,
(id)newObj, location,
CrashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
......
}
......
}
// 先简单看下weak_table结构体
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
// 在weak_table中反注册weak变量
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer); // 从weak_table中移除相关entry实现反注册
......
}
......
}
// 在weak_table中注册weak变量
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
if (!referent || referent->isTaggedPointer()) return referent_id;
......
// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer); // 如果相应的entry已经存在,那么继续添加
}
else {
weak_entry_t new_entry;
new_entry.referent = referent;
new_entry.out_of_line = 0;
new_entry.inline_referrers[0] = referrer;
for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
new_entry.inline_referrers[i] = nil;
}
weak_grow_maybe(weak_table);
// 如果相应的entry不存在,那么创建新的entry,并插入weak_table实现注册
weak_entry_insert(weak_table, &new_entry);
}
......
}
上面的代码片段,完整的展示了SideTable如何管理weak变量。
Objc重要的宏定义
SUPPORT_GC
#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || (TARGET_OS_MAC && __x86_64h__)
# define SUPPORT_GC 0
#else
# define SUPPORT_GC 1
#endif
可以发现,现在都是不支持GC的
SUPPORT_TAGGED_POINTERS
唐巧-深入理解Tagged Pointer wikipedia-Tagged Pointer
// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects
// Be sure to edit tagged pointer SPI in objc-internal.h as well.
#if !(__OBJC2__ && __LP64__)
# define SUPPORT_TAGGED_POINTERS 0
#else
# define SUPPORT_TAGGED_POINTERS 1
#endif
可以发现,只有64位机器才是支持Tagged Pointer的。
SUPPORT_NONPOINTER_ISA
// Define SUPPORT_NONPOINTER_ISA=1 to enable extra data in the isa field.
#if !__LP64__ || TARGET_OS_WIN32 || TARGET_IPHONE_SIMULATOR
# define SUPPORT_NONPOINTER_ISA 0
#else
# define SUPPORT_NONPOINTER_ISA 1
#endif
可以发现只有64位真机才支持NonPointerISA,模拟器是不支持的。
NSObject (刨根问底Objective-c Runtime)
self
细心的开发者可能发现,在类方法和实例方法中,都能使用self,那这两个self有什么不一样呢?
+ (id)self {
// 类方法的self是指class instance
return (id)self;
}
- (id)self {
// 实例方法的self是指the instance of the class
return self;
}
从上图可以看到,instance是Class的实例,而Class是Meta Class的实例,这样上面两个self就比较清晰了。类方法的self,其实指向的是Meta Class的实例,也就是NSObject;而实例方法的self,其实指向的是Class的实例,也就是NSObject的实例。
Class && id
既然说到class,那就聊聊class到底是什么鬼,顺便聊聊常用的id是什么鬼。
typedef struct objc_class *Class;
typedef struct objc_object *id;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
仔细查看上面的代码可以发现,id其实就是objc_object *
, 也就是Class的实例,通过isa指针可以找到实例对应的类。
比较复杂的是Class,其原型是objc_class
,可以发现其也有一个isa指针,说明类其实也是其他类的实例,而这个其他类,就是我们上文提到的meta class。objc_class
结构体中还存储了类对象的实例变量列表、方法列表以及协议列表等,这些都可以在运行时改变。
那如何判断一个Class是否是meta Class呢?下面我们就看下objc-runtime-new.h
下的新的objc_class的定义。
// 这是新的objc_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
......
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;
}
......
};
是不是非常直观,objc_class结构体是继承自objc_object结构体的,而判断是够是MetaClass是由data中的一个bit位来决定的。
既然我们了解了instance、Class和Meta Class的关系,也了解了objc_class
和objc_object
的结构,接下来我们就来看几个比较有意思的方法,也是我们平常比较常用的方法。
isMemberOfClass
VS isKindOfClass
// 判断是够是当前类的实例
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
// 判断是否是当前类或者其父类的实例
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
respondsToSelector
// 判断当前对象是否能够响应某个函数
- (BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector_inst([self class], sel, self);
}
bool class_respondsToSelector_inst(Class cls, SEL sel, id inst)
{
IMP imp;
// 如果没有指定方法或者类,返回NO
if (!sel || !cls) return NO;
// Avoids +initialize because it historically did so.
// We're not returning a callable IMP anyway.
imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, YES/*resolver*/);
return bool(imp);
}
// 查找Sel对用的IMP实现,可以指定是否使用cache(这里cache是为了加快访问速度,后续就不再展现这部分逻辑)
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
......
while ((curClass = curClass->superclass)) {
......
// 从当前类以及其父类中寻找sel对应的实现
meth = _class_getMethodNoSuper_nolock(curClass, sel);
......
}
......
}
static Method _class_getMethodNoSuper_nolock(Class cls, SEL sel)
{
methodListLock.assertLocked();
return (Method)_findMethodInClass(cls, sel);
}
static inline old_method * _findMethodInClass(Class cls, SEL sel) {
.......
// 可以发现,method的寻找是从之前我们提到的cls结构体中的methodList中取出的
mlistp = (old_method_list **)&cls->methodLists;
*mlistp = fixupSelectorsInMethodList(cls, *mlistp);
return _findMethodInList(*mlistp, sel);
......
}
conformsToProtocol:
// 判断实例能否响应某个protocol
- (BOOL)conformsToProtocol:(Protocol *)protocol {
if (!protocol) return NO;
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (class_conformsToProtocol(tcls, protocol)) return YES;
}
return NO;
}
BOOL protocol_conformsToProtocol(Protocol *self_gen, Protocol *other_gen)
{
if (self->protocol_list) {
int i;
for (i = 0; i < self->protocol_list->count; i++) {
old_protocol *proto = self->protocol_list->list[i];
// 比较class的protocol_list中有的protocol_name和传参name
if (0 == strcmp(other->protocol_name, proto->protocol_name)) {
return YES;
}
if (protocol_conformsToProtocol((Protocol *)proto, other_gen)) {
return YES;
}
}
}
return NO;
}
performSelector:
+ (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
// 其实质就是直接调用C的函数objc_msgSend
return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
}
动态方法解析
参考资料
- objc源码
- «Effective Objective-C 2.0»