探索方法归属以及类的归属(5)
Opened this issue · 0 comments
类存在几份?
- 由于
类的信息
在内存
中永远只存在一份
,所以类对象只有一份
objc_object 与 对象的关系
- 所有的
对象
都是以objc_object
为模板继承
过来的 - 所有的对象 是 来自
NSObject
(OC) ,但是真正到底层的 是一个objc_object(C/C++)
的结构体类型 objc_object
与对象
的关系
是继承
关系
什么是 属性 & 成员变量 & 实例变量 ?
-
(property):在OC中是通过
@property开头定义
,且是带下划线成员变量
+setter
+getter
方法的变量 -
成员变量
(ivar):在OC的类中{}中定义
的,且没有下划线
的变量 -
实例变量
:通过当前对象类型,具备实例化的变量
,是一种特殊的成员变量
,例如 NSObject、UILabel、UIButton等
成员变量 和 实例变量什么区别?
-
实例变量
(即成员变量
中的对象变量
就是实例变量
):以实例对象实例化来的,是一种特殊的成员变量
-
NSString
是常量
类型, 因为不能添加属性
,如果定义在类中的{}
中,是成员变量
-
成员变量
中 除去基本数据类型、NSString
,其他都是实例变量
(即可以添加属性
的成员变量
),实例变量主要是判断是不是对象
探索方法的归属
class_getInstanceMethod
方法
void InstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
// Student 实现
@implementation Student
- (void)sayHello {
}
+ (void)sayHappy {
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student = [Student alloc];
Class pClass = object_getClass(student);
InstanceMethod_classToMetaclass(pClass);
}
return 0;
}
// 输出结果为
// InstanceMethod_classToMetaclass - 0x1000030e0-0x0-0x0-0x100003078
这个方法 class_getInstanceMethod
我们从字面上的意思去理解这个方法,应该是 获取某个类的实例方法,我们根据 isa
走位图,可以得出,实例方法存储在类里面,类方法存储在元类。
class_getInstanceMethod
的源码实现
/***********************************************************************
* class_getInstanceMethod. Return the instance method for the
* specified class and selector.
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
- 当我们调用class_getInstanceMethod方法时, 它在源码里会根据传入的cls, 来查找它的结构体里有没有我们要获取的方法。
class_getInstanceMethod(pClass, @selector(sayHello));
这段代码, 就是要让我们根据cls类的bits里, 来查找是否有sel
这个实例方法, 验证(平移cls内存地址获取bits, 打印methods)后, 发现是有这个方法的。class_getInstanceMethod(metaClass, @selector(sayHello));
这段代码, 发现元类里是没有sayHello方法的, 但是我们找到了sayHappy的方法。- 在源码的世界里, 只有实例方法, 是没有类方法这个说法的(OC层面才会有这些说法), 因为每个类每个元类, 他们都是对象, 他们的方法都存储在他们的父级里。
class_getClassMethod
方法
void ClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student = [Student alloc];
Class pClass = object_getClass(student);
ClassMethod_classToMetaclass(pClass);
}
return 0;
}
// 输出结果为
// ClassMethod_classToMetaclass - 0x0-0x0-0x100003078-0x100003078
class_getClassMethod
的源码实现
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
其实 class_getClassMethod
方法最终也是调用到class_getInstanceMethod
方法里面来。只是传进去的 class
会变成传递 class
的 MetaClass
。
为什么method4
能正常输出结果呢,这就要根据源码来分析了,我们看到去查找方法的时候,传入的是 cls->getMeta()
,我们跟进去看他究竟是怎样实现的
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
这样就非常清晰了,注释都已经提示我们了,如果我们传入的 class
是 MetaClass
就直接返回。
那么为什么苹果这样设计呢,其实就是为了防止 无限递归,所以就知道为什么是这样的输出结果了。
method1: 类 -> 元类 -> 元类(
isMetaClass
返回自身)中没有sayHello实例方法.
method2: 元类-> 元类(isMetaClass
返回自身)中没有sayHello实例方法.
method3: 类 -> 元类 -> 元类(isMetaClass
返回自身)中有sayHappy类方法
method4: 元类 -> 元类(isMetaClass
返回自身)中有sayHappy类方法
class_getMethodImplementation
方法
void IMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%s - %p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student = [Student alloc];
Class pClass = object_getClass(student);
IMP_classToMetaclass(pClass);
}
return 0;
}
// 输出结果为
// IMP_classToMetaclass - 0x100001990-0x7ff6b57d580-0x7ff6b57d580-0x100001990
class_getMethodImplementation
的源码实现
/**
* Returns the function pointer that would be called if a
* particular message were sent to an instance of a class.
*
* @param cls The class you want to inspect.
* @param name A selector.
*
* @return The function pointer that would be called if \c [object name] were called
* with an instance of the class, or \c NULL if \e cls is \c Nil.
*
* @note \c class_getMethodImplementation may be faster than \c method_getImplementation(class_getInstanceMethod(cls, name)).
* @note The function pointer returned may be a function internal to the runtime instead of
* an actual method implementation. For example, if instances of the class do not respond to
* the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
*/
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward; // 0x7ff6b57d580 实际上就是它
}
return imp;
}
地址出现了2次:0x7fff6b57d580,这个是在调用class_getMethodImplementation()方法时,无法找到对应实现时返回的相同的一个地址,无论该方法是在实例方法或类方法,无论是否对一个实例调用该方法,返回的地址都是相同的,但是每次运行该程序时返回的地址并不相同。
如果向一个类的实例发送一条消息,该函数会返回该条消息的 IMP
。class_getMethodImplementation
可能比method_getImplementation
更高效。
返回的指针可能会是一个方法的 IMP
,也可能是runtime
内部的一个函数。比如说,如果一个类的对象不能响应一个 selector
,这个函数指针返回的就会是 runtime
里面消息转发机制的一部分。
method1: 类中有sayHello实例方法, 所以也查找到它的imp
method2: 元类中没有sayHello实例方法, 返回msg_forward_name
地址
method3: 类中没有sayHappy类方法, 返回msg_forward_name
地址
method4: 元类有sayHappy类方法, 所以也查找到它的imp
探索isKindOfClass
和isMemberOfClass
isKindOfClass
探索
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[Student class] isKindOfClass:[Student class]];
BOOL re4 = [(id)[Student class] isMemberOfClass:[Student class]];
NSLog(@" \nre1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[Student alloc] isKindOfClass:[Student class]];
BOOL re8 = [(id)[Student alloc] isMemberOfClass:[Student class]];
NSLog(@" \nre5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
分析一下源码这两个函数的对象实现
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
inline Class
objc_object::ISA()
{
return (Class)(isa.bits & ISA_MASK);
}
+ (BOOL)isMemberOfClass:(Class)cls { // 通过与查找自身的元类, 并与自身进行对比
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls { // 通过与查找自身的类型, 并与自身进行对比
return [self class] == cls;
}
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
}
objc_opt_isKindOfClass
通过查找obj的元类, 并递归循环
元类的superclass
与otherClass进行对比。
需要注意isa也就是元类的继承关系, 是的
元类也存在继承关系
:
元类 -> 根元类(metaClass) -> 根类(NSObject) -> nil
re1 结果分析
-
+ (BOOL)isKindOfClass:(Class)cls
方法,会先调用objc_opt_isKindOfClass(id obj, Class otherClass)
,为什么会调用这个呢,因为是LLVM
优化的结果,然后会调用ISA()
方法中获得MetaClass
的指针。 -
接着在
isKindOfClass
中有一个循环,先判断class
是否等于MetaClass
,不等就继续循环判断是否等于superclass
,不等再继续取superclass
,如此循环下去。 -
[NSObject class]
执行完之后调用isKindOfClass
,第一次判断先判断NSObject
和NSObject
的MetaClass
是否相等,从之前的isa
的走位图上我们也可以看出,NSObject
的MetaClass
与本身不等。接着第二次循环判断NSObject
与MetaClass
的superclass
是否相等。还是从那张图上面我们可以看到:Root class(meta)
的superclass
就是Root class(class)
,也就是NSObject
本身。所以第二次循环相等,于是re1
输出应该为YES。
re3 结果分析
[Student class]
执行完之后调用objc_opt_isKindOfClass
,然后开始循环遍历,第一次for
循环,Student
的MetaClass
与[Student class]
不等,第二次for
循环,Student(MetaClass)
的superclass
指向的是NSObject(MetaClass)
, 和[Student class]
不相等。第三次for
循环,NSObject(MetaClass)
的superclass
指向的是NSObject(Class)
,和[Student class]
不相等。第四次循环,NSObject(Class)
的superclass
指向 nil, 和[Student class]
不相等。第四次循环之后,退出循环,所以re3
输出为 NO。
re2 结果分析
+ (BOOL)isMemberOfClass:(Class)cls
方法内部,会先调用ISA()
方法中获得MetaClass
的指针,然后直接比较当前类的MetaClass
是否等于[NSObject class]
,所以re2
输出为 NO
re4 结果分析
- 比较的是
Student(MetaClass)
和[Student class]
,所以re4
输出为 NO
re5 结果分析
- 此时应该输出 YES,因为在
objc_opt_isKindOfClass
函数中,判断NSObject
的isa
指向是否是自己的类NSObject
,第一次for
循环就能输出 YES 了。
re7 结果分析
- 同上,第一次
for
循环就输出 YES。
re6 结果分析
- 因为在
- (BOOL)isMemberOfClass:(Class)cls
函数中,获取当前类的class
与[NSObject class]
进行比较,输出结果为 YES。
re8 结果分析
- 同理,输出结果为 YES。
最后
给大家一份练习题,以下这段代码的运行会崩溃吗?
@interface Teacher : NSObject
+ (void)sayByebye;
@end
@implementation Teacher
- (void)sayByebye {
NSLog(@"88");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[Teacher sayByebye];
}