Class、objc_object、id的定义,都基于objc_class,一个NSObject对象的就是objc_object类型的结构体指针objc_object *
typedef struct objc_class *Class; //objc_class的结构体指针objc_class *,重定义一个名字Class
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id; //重定义一个NSObject对象名字id,类型为objc_object *
可以看到一个NSObject对象为objc_object类型,里面有8个字节的结构体指针isa,而alloc探究的时候,得知实际实际上给NSObject对象申请的的空间至少为16字节,除了isa之外,并且新增加的属性字段内容就存放在里面,且数据就在isa后面剩余的空间地址存放,下面探究一下对象的数据存放
先从objc_object开始(下面均以objc-750为例探究)
objc_object
通过NSObject类型可以得到,存放的是带有Class参数的Object_object结构体指针,而里面的Class代表的是对象的所属类(后面介绍),那么其参数和isa是怎么区分开的呢,参考下面一段代码
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();
发现里面存在一个 isa_t类型的isa,似乎有什么玄机,点击查看其结构
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
发现了一个联合体,顿时豁然开朗,原来isa和数据都存放在一个联合体中,其共享一片内存,系统通过联合体,将Class类型的isa和其他属性内容存放到了里面的,并且通过某种手段处理其信息,合理区分,并且可以拿出
参考上面的获取ISA()方法,可以看到如何从该联合体中拿出isa的
# define ISA_MASK 0x00007ffffffffff8ULL
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#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的过程,TaggedPointer这个特殊结构先不提,后面再讲,可以看到最终的返回了这么一句:
return (Class)(isa.bits & ISA_MASK);
而ISA_MASK的值为0x00007ffffffffff8ULL,通过将isa.bits地址的内容按位与ISA_MASK,即可得到isa的地址
而其他的属性内容就存放在isa地址后面的剩余空间,系统通过类似的手段将字段内的值读取出来
objc_class
前面可以看到Class是一个objc_class类型的结构体指针,并且NSObject对象中只保留了属性字段内容,其方法、类方法、属性方法、属性名字协议等均不在对象中,那么只能在objc_class类型的isa中寻找答案了
下面查看一下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
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
这个就是isa的方法,发现其继承自objc_object,里面也有一个isa,其isa获取过程同对象一样,里面存放的为元类的结构体指针
superclass 则为其父类的结构体指针,占用8字节
cache 为快速缓存,为了快速调用方法,占用16字节,可以查看其结构
bits 通过data方法可以知道从里面获取到了class_rw_t *类型的数据
前面给了参数的字节大小,可以通过class的地址直接向后偏移32位,即:8 + 8 + 16字节(注意最新的可能就不是这个了),即可找到bits参数,然后通过data方法可以找到class_rw_t,然后可以看到ro结构
那么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));
}
};
顿时茅塞顿开,原来这里面就是存放了方法、属性、协议等的地方
里面存放了该class方法、属性、协议(注意不包括对象的类方法,类方法等在元类的rw中),且以method_array_t类型的methods为例,其为二级指针类型,后面介绍
紧接着,rw里面还有一个const类型的class_ro_t结构体指针,里面也存放了了方法、属性、协议、ivars列表
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;
}
};
ro被const修饰,里面的数据为编译器就已经确定了的方法、属性、字段、协议方法(注意里面是对象方法,类方法等在元类的ro中),加入到数组中,当加载应用时,方法、协属性会被加载到rw里面
在ro的方法列表中,里面存放了每个方法的名称、类型(包括返回值、参数等简称,例如:v16@0:8)、imp指针
method、ivar和property等结构源码如下
struct method_t {
SEL name;
const char *types;
MethodListIMP imp;
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
struct property_t {
const char *name;
const char *attributes;
};
介绍完ro,查看一下rw中method_array_t等类型,其都继承自list_array_tt类型,为一个二维数组,以method为例,还额外添加了beginCategoryMethodLists,endCategoryMethodLists等方法
methods:
class method_array_t :
public list_array_tt<method_t, method_list_t>
{
typedef list_array_tt<method_t, method_list_t> Super;
public:
method_list_t **beginCategoryMethodLists() {
return beginLists();
}
method_list_t **endCategoryMethodLists(Class cls);
method_array_t duplicate() {
return Super::duplicate<method_array_t>();
}
};
properties:
class property_array_t :
public list_array_tt<property_t, property_list_t>
{
typedef list_array_tt<property_t, property_list_t> Super;
public:
property_array_t duplicate() {
return Super::duplicate<property_array_t>();
}
};
protocols:
class protocol_array_t :
public list_array_tt<protocol_ref_t, protocol_list_t>
{
typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;
public:
protocol_array_t duplicate() {
return Super::duplicate<protocol_array_t>();
}
};
下面查看一下list_array_tt类型,发现里面定义了二维数组的一些基本操作方法,其中也包括往里面添加一个数组attachLists,后面不少地方会用到,attachLists方法会将后添加进入的数组,放到原始数组的前面
uint32_t count() {
uint32_t result = 0;
for (auto lists = beginLists(), end = endLists();
lists != end;
++lists)
{
result += (*lists)->count;
}
return result;
}
iterator begin() {
return iterator(beginLists(), endLists());
}
iterator end() {
List **e = endLists();
return iterator(e, e);
}
uint32_t countLists() {
if (hasArray()) {
return array()->count;
} else if (list) {
return 1;
} else {
return 0;
}
}
List** beginLists() {
if (hasArray()) {
return array()->lists;
} else {
return &list;
}
}
List** endLists() {
if (hasArray()) {
return array()->lists + array()->count;
} else if (list) {
return &list + 1;
} else {
return &list;
}
}
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// 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;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
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]));
}
}
然后查看用到此方法的函数,addMethod、addMethods、class_addProtocol、_class_addProperty、attachCategories,以addMethod为例,发现新增加的方法,通过attachLists直接被添加到rw的method里面去了
static IMP
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
IMP result = nil;
runtimeLock.assertLocked();
checkIsKnownClass(cls);
assert(types);
assert(cls->isRealized());
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
// already exists
if (!replace) {
result = m->imp;
} else {
result = _method_setImplementation(cls, m, imp);
}
} else {
// fixme optimize
method_list_t *newlist;
newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
newlist->entsizeAndFlags =
(uint32_t)sizeof(method_t) | fixed_up_method_list;
newlist->count = 1;
newlist->first.name = name;
newlist->first.types = strdupIfMutable(types);
newlist->first.imp = imp;
prepareMethodLists(cls, &newlist, 1, NO, NO);
cls->data()->methods.attachLists(&newlist, 1);
flushCaches(cls);
result = nil;
}
return result;
}
然后查看methodizeClass方法,其在应用加载的时候,会将ro里面的baseMethods、baseProtocols、baseProperties均加载到了rw里面
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
if (PrintConnecting) {
if (cats) {
for (uint32_t i = 0; i < cats->count; i++) {
_objc_inform("CLASS: attached category %c%s(%s)",
isMeta ? '+' : '-',
cls->nameForLogging(), cats->list[i].cat->name);
}
}
}
if (cats) free(cats);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
assert(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
最后还调用了attachCategories方法,将类别里面的属性、协议、方法都加入到了rw里面,由于attachLists会把后加入的数组放到列表最前面,因此通过类别添加的方法,会被放置到方法列表的最前面。
因此你可能想到了,为什么同名会优先调用分类的方法了,毕竟方法查找是从前往后找的,找到即执行
追加分类的方法如下所示:
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);
}
最后
可以根据前面的方法,分别打印出改类ro中的方法和其元类的ro方法
可以发现,对象方法均储存在类或者父类中,类方法均储存在元类或者元类的父类中
isa目前就先探究到这里了