iOS-底层(7):isa相关问题答疑
今天朋友问了我两道面试题,我们来看第一道
第一道
假设我们有一个FYPerson
类,并且- (void)sayHello
和+ (void)sayHappy
都有实现
#import <Foundation/Foundation.h>
#import "FYPerson.h"
#import <objc/runtime.h>
#ifdef DEBUG
#define FYLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define FYLog(format, ...);
#endif
void fyObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
FYLog(@"Method, name: %@", key);
}
free(methods);
}
void fyInstanceMethod_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));//有
FYLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void fyClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));//类的元类中不包含sayHello实例方法
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));//元类中不包含sayHello实例方法
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));//类的元类中包含sayHappy类方法
// 元类 为什么有 sayHappy 类方法 0 1
//
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));///元类中包含sayHappy实例方法
FYLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void fyIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
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(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
FYPerson *person = [FYPerson alloc];
Class pClass = object_getClass(person);
fyObjc_copyMethodList(pClass);
fyInstanceMethod_classToMetaclass(pClass);
fyClassMethod_classToMetaclass(pClass);
fyIMP_classToMetaclass(pClass);
}
return 0;
}
打印结果
image.png- class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) 返回类的对象方法
/***********************************************************************
* 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_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
返回类的类方法
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
可以看出此方法实际调用的是 class_getInstanceMethod(cls->getMeta(), sel);
,也就是返回类的元类的实例方法,这也说明类方法就是元类的实例方法
我们看看cls->getMeta()
做了什么
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
如果是元类直接返回了自己,因为我们类方法最终存在于元类这一层,就不用继续往下找了,防止了无限递归
-
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
返回方法的实现
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;
}
return imp;
}
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));
打印结果为0x100001d00-0x7fff6cac3580-0x7fff6cac3580-0x100001d30,imp1和imp4都找到了方法实现,类里面包含实例方法,元类里面包含类方法,那么为什么imp2和imp3确是一样的呢?
通过打印我们看到,其实这两个为_objc_msgForward方法的地址,因为元类里面不包含sayHello的对象方法,类里面同样找不到sayHappy类方法,所以就会来到消息转发流程,来到
_objc_msgForward
,至于_objc_msgForward到底是什么,我会在后面的对象的消息机制中讲到
第二道
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //1
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //0
BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]]; //0
BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]]; //0
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //1
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //1
BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]]; //1
BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]]; //1
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
image.png
和你想的结果一样吗?
我们来分析一下这道题
首先我们了解一下isa走位和类的继承关系,看这张图,这张图不理解的可以看我的这篇文章,iOS-底层(6):类的结构分析
image.png要知道打印结果,我们要看一下这四个方法的底层实现:
+ (BOOL)isMemberOfClass:(Class)cls {
//传入的类和类对象的元类比较
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
//传入的类依次和对象所属的元类比较
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
//传入的类依次和类对象的元类 - >元类的父类->NSobject根源类->NSobject 比较
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
//传入的类依次和对象的所属的类 - >类的父类->NSobject 比较
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
-
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
- 调用
+(BOOL)isKindOfClass:(Class)cls
类方法,首先tcls
被赋值为self(NSObject)->ISA()
和 传入的NSObject
类比较,根据isa
走位图可知tcls
为NSObject根元类
,tcls != cls
,tcls = tcls->superclass
,NSObject根元类
的父类是NSObject
,进入下层循环,tcls == cls
成立returen YES
;
- 调用
-
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
- 这个比较简单就是判断 self->ISA() == cls?很明显 根元类 不等于 NSObject
-
BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];
- Person的isa为
Person元类
, tcls依次等于Person元类
->NSObject根元类
->NSObjec
,都不等于Person类。
- Person的isa为
-
BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];
- 判断条件依然是
self->ISA() == cls Person元类 != Person
- 判断条件依然是
-
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //1
- 这里调用的是- (BOOL)isKindOfClass:(Class)cls对象方法,
[self class](NSObject) == cls(NSObject)
- 这里调用的是- (BOOL)isKindOfClass:(Class)cls对象方法,
-
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //1
- 这里调用的是- (BOOL)isMemberOfClass:(Class)cls对象方法,判断条件:[self class] == cls
[self class](Person) == cls(Person)
成立
- 这里调用的是- (BOOL)isMemberOfClass:(Class)cls对象方法,判断条件:[self class] == cls
-
BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]]; //1
- 这里调用的是- (BOOL)isKindOfClass:(Class)cls对象方法,
[self class](Person) == cls(Person)
- 这里调用的是- (BOOL)isKindOfClass:(Class)cls对象方法,
-
BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]]; //1
- 这里调用的是- (BOOL)isMemberOfClass:(Class)cls对象方法,判断条件:[self class] == cls
[self class](Person) == cls(Person)
成立
- 这里调用的是- (BOOL)isMemberOfClass:(Class)cls对象方法,判断条件:[self class] == cls
其实上面的方法并不会直接进入,打开汇编我们发现实际调用的是objc_opt_isKindOfClass
这个方法更明显,也就是说 isKindOfClass 是拿传入了的类和对象isa的继承链来比较
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
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;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
拓展
-
由于类的信息在内存中永远只存在一份,所以 类对象只有一份
-
所有的对象 都是以 objc_object为模板继承过来的,所有的对象 是 来自 NSObject(OC) ,但是真正到底层的 是一个objc_object(C/C++)的结构体类型
属性 & 成员变量 & 实例变量
-
属性(property):在OC中是通过@property开头定义,且是带下划线成员变量 + setter + getter方法的变量
-
成员变量(ivar):在OC的类中{}中定义的,且没有下划线的变量
-
实例变量:通过当前对象类型,具备实例化的变量,是一种特殊的成员变量,例如 NSObject、UILabel、UIButton等
-
成员变量 和 实例变量什么区别?
- 实例变量(即成员变量中的对象变量 就是 实例变量):以实例对象实例化来的,是一种特殊的成员变量
NSString 是常量类型, 因为不能添加属性,如果定义在类中的{}中,是成员变量
成员变量中 除去基本数据类型、NSString,其他都是 实例变量(即可以添加属性的成员变量),实例变量主要是判断是不是对象