在分析isa结构体之前,我们应该先思考一下是什么?
首先,我们需要利用Clang来编译OC,Clang编译使用在这篇文章中介绍了
1.对象本质的探究
建立工程,在maim.m中创建一个XpPerson类
@interface XpPerson : NSObject {
NSString *nickName;
}
@property (nonatomic, copy) NSString *name;
@end
@implementation XpPerson
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
XpPerson *objc = [XpPerson alloc];
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
通过Clang编译main.cpp文件,我们可以看到XpPerson类
main.cpp文件.jpg
- 根据上图,我们可以明显看得出XpPerson类类对象在底层中被编译成了一个结构体
extern "C" unsigned long OBJC_IVAR_$_XpPerson$_name;
struct XpPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *__strong nickName;
NSString *__strong _name;
};
- 因为在C++中结构体是可以继承的,中的第一个属性其实就是 isa,是继承自NSObject,属于伪继承,伪继承的方式是直接将NSObject结构体定义为中的第一个属性,意味着拥有 NSObject中的所有成员变量。
- 对象的属性name方法,在OC中会自动带有和方法在底层通过objc_setProperty实现
static NSString * _I_XpPerson_name(XpPerson * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_XpPerson$_name)); }
static void _I_XpPerson_setName_(XpPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct XpPerson, _name), (id)name, 0, 1); }
总结
- 就是
- 中的就是继承自中的
1.对objc_setProperty的探究
底层runtime通过objc_setProperty设置对象属性的sette和getter方法,下载objc4_781源码搜索objc_setProperty
搜索objc_setProperty.jpg
找到objc_setProperty方法,我们可以看到其通过reallySetProperty方法实现,再点击reallySetProperty进去
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
通过查看objc_setProperty方法的实现,发现上层属性的set方法到底层的set方法经过objc_setProperty方法处理之后,已经失去了痕迹,只是带进来了每个set方法特有的_cmd,可想而知,objc_setProperty就是上层set和下层set的一个中间关联层
- objc_setProperty 是关联上层set和下层set的一个中间接口
-
基于上述原因,苹果采用了适配器设计模式(即将底层接口适配为客户端需要的接口),对外提供一个接口,供上层的set方法使用,对内调用底层的set方法,使其相互不受影响,即无论上层怎么变,下层都是不变的,或者下层的变化也无法影响上层,主要是达到上下层接口隔离的目的
01.jpg
3.isa
- nonpointer 情况:isa_t newisa(0);
- !nonpointer情况: isa = isa_t((uintptr_t)cls);
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);//初始化
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
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;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
在源码查看isa_t结构体,我们也可以知道其实isa_t中的 cls 和 bits 其实是互斥的。当我们的类型是nonpointer时,bits会有值,当不是nonpointer时,会直接返回cls
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
};
网友评论