对象与方法的本质
Objective-C 对象Class
的本质是结构体。
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class
const char *name
long version
long info
long instance_size
struct objc_ivar_list *ivars
struct objc_method_list **methodLists
struct objc_cache *cache
struct objc_protocol_list *protocols
#endif
} OBJC2_UNAVAILABLE;
NSObject的创建过程
我们都知道我们初始化一个对象都是这样子
NSObject *obj = [[NSObject alloc]init];
那么alloc 做了什么? init 又做了什么呢?
-
alloc
我们可以从开发者中心下载到苹果开源的代码,我们这里举例使用的是objc750
我们通过LLDB调试可以看到,对象alloc时,在汇编中调用了_objc_rootAlloc
,明显这就是在苹果源码中的调用入口。
这里补充一下[obj new],相信聪明的你已经懂了。
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
LLDB继续往下走,可以发现在_objc_rootAlloc
又调用了class_createInstance
,我们在源码中跳转也能找到相关的方法
+ (id)alloc {
return _objc_rootAlloc(self);
}
// ************* 跳转 **************
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
// ************* 跳转 **************
可见对象alloc的根本原理就在这个方法里面了!
- class_createInstance
这个方法最终会来到这里。记 得 看 注 释 。
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
// 一些判断参数 包括两个c++析构器和"是否可以创建Nonpointer",打断点进来之后,fast为true
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
// 计算对象所需要的内存空间
// 这里有涉及字节对齐,iOS中以8字节对齐,
// 什么意思呢,size必须为8的倍数,假设对象所需内存空间为12字节,那么这个size就是16。
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
// zone = nil && fast为true
if (!zone && fast) {
//开辟内存空间,开辟后,obj还不具有NSObject该有的功能
obj = (id)calloc(1, size);
if (!obj) return nil;
// 初始化isa
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
- 啥是isa
isa是一个指针。在Objective-C中,任何类的定义都是对象。类和类的实例没有任何本质上的区别。任何对象都有isa指针。
obj->initInstanceIsa(cls, hasCxxDtor);
最终将会来到
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
// 如果不做nonpointer优化,
// nonpointer优化是啥?
// 使isa地址中包含了类信息、对象的引用计数等。
// 这里传入的nonpointer 为true
if (!nonpointer) {
isa.cls = cls;
} else {
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
// new一个isa_t
// isa_t是一个联合体包含了这些变量
// uintptr_t nonpointer : 1;表示是否对 isa 指针开启指针优化 ,
// uintptr_t has_assoc : 1;关联对象标志位,0没有,1存在。
// uintptr_t has_cxx_dtor : 1;该对象是否有 C++ 析构器,如果有,则需要做析构逻辑,没有,则可以更更快的释放对象。
// uintptr_t shiftcls : 33;存储类指针的值。
// uintptr_t magic : 6;用于调试器判断当前对象是真的对象还是没有初始化的空间 。
// uintptr_t weakly_referenced : 1;标志对象是否被指向或者曾经指向⼀一个 ARC 的弱变量,没有弱引用的对象可以更快释放
// uintptr_t deallocating : 1;标志对象是否正在释放内存。
// uintptr_t has_sidetable_rc : 1;标志对象是否有用到散列表,当对象引⽤计数大于 10 时,则需要借⽤该变量存储进位。
// uintptr_t extra_rc : 19;当表示该对象的引用计数值,实际上是引用计数值减 1,例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要用 has_sidetable_rc
// 在64位的机器上,uintptr_t = unsigned long int
// 在32位的机器上,uintptr_t =unsigned int
isa_t newisa(0);
//一系列 isa参数赋值
// 其中 isa.magic isa.nonpointer 都包含在
// 中ISA_MAGIC_VALUE
#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中的具体参数
到了这里,obj的alloc就完整了。
iOS中,还有一个初始化方法allocWithZone,当然,我们在上文也有出现过,实际上alloc跟allocWithZone走的是同一个方法,只是参数区别在于一个zone,实际上zone已经被系统忽视(ignore)了,官方文档也默认让传入nil,所以,我们在平时创建出来的对象,不论你是否有传zone,官方都默认无视。
那么init呢?
- init
实际上 init 只是一个抽象方法,并没有做什么操作。
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
所以当我们对象alloc了之后,就一直可以对alloc出来的对象操作了。如下图。(工程名请无视 ^ _ ^ )
Objective-C 方法Method的本质
本质是发送消息,任何的方法的调用都会编译成消息。
消息的组成:
objc_msgSend(id self, SEL _cmd, ...)
消息接受者,消息编号,参数 (消息体)
objc_msgSend(id self, SEL _cmd, ...)
消息的组成由 id self,SEL _cmd 以及后续的参数其中,
id self 可以理解为调用方法的对象,消息接受者,即下文中的 "s"。
SEL _cmd 为方法名,传入类似sel_registerName("MethodName"),即下文中的"[s walk];"方法。
若方法中需要传入参数,就在后面拼接,即下文中的 "[s say:]"方法。
获取SEL的三种方法:
1. Runtime提供的 sel_registerName
2. Objective-C编译器提供的 @selector()
3. NSObject提供的 NSSelectorFromString()
SEL与IMP(implementation)的关系:
sel_registerName("MethodName")为方法编号,通过方法编号找到方法对应的IMP是函数实现的指针,
最后直接调用函数。
比喻:通过书页(SEL) 找到该页的具体内容 (IMP)
- 实例方法调用
Student *s = [Student new];
[s walk];
objc_msgSend(s, @selector(walk));
//带参数
[s say:@"Hello World"];
objc_msgSend(s, sel_registerName("say:"), "Hello World");
- 类方法调用
[Student run];
objc_msgSend(objc_getClass("Student"), sel_registerName("run"));
//带参数
[Student say:@"Hello World"];
objc_msgSend(objc_getClass("Student"), sel_registerName("say:"), "Hello World");
- 如何给父类发消息
Student *s = [Student new];
// 向父类发消息(对象方法)
struct objc_super superClass;
superClass.receiver = s; //确定这个消息发送的对象,即发送给s的父类
superClass.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(& superClass, sel_registerName("walk"));
//向父类发消息(类方法)
struct objc_super superClass;
superClass.receiver = [s class];
superClass.super_class = class_getSuperclass(object_getClass([s class]));
objc_msgSendSuper(& superClass, sel_registerName("run"));
//带参数
objc_msgSendSuper(& superClass, sel_registerName("say:"),@"Hello World");
- 问答
问:对象方法和类方法存在哪?
答:对象方法存在类里,类方法存在元类中
问:“类方法”在元类中是以什么样的姿态存在
答:实例方法。因为:对象在类中是实例,类在元类中也是对象、实例,参考:isa走位图