iOS-底层探索06:isa 经典面试题分析
目录
- 一、类在内存中存在几份
- 二、objc_object 与 对象的关系
- 三、什么是 属性 & 成员变量 & 实例变量 ?
- 四、成员变量 和 实例变量什么区别?
- 五、元类 中为什么会有 类对象 的 类方法?
- 六、实例对象、类对象、元类对象到底有什么区别呢?
- 七、探究class_copyMethodList等方法
- 八、iskindOfClass & isMemberOfClass 的理解
- 九、iOS中内省的几个方法?
一、类在内存中存在几份?
类在内存中存在几份实际上是说类对象在内存中存在几份。由于类的信息
在内存中永远只存在一份
,所以 类对象只有一份
。
二、objc_object 与 对象的关系
-
所有的
对象
都是以objc_object
为模板继承过来的 -
所有的对象 是 来自
NSObject(OC)
,但是真正到底层的 是一个objc_object(C/C++)
的结构体类型
【总结】objc_object
与 对象
的关系 是 继承关系
三、什么是 属性 & 成员变量 & 实例变量 ?
-
属性(property)
:在OC
中是通过@property
开头定义,属性
=ivar
+setter
+getter
-
成员变量(ivar)
:在OC
的类中{}
中定义的 -
实例变量
:通过当前对象类型,具备实例化的变量,是一种特殊的成员变量
,例如NSObject
、UILabel
、UIButton
等
四、成员变量 和 实例变量什么区别?
-
实例变量
(即成员变量
中的对象变量
就是实例变量
):以实例对象实例化来的,是一种特殊的成员变量
-NSString
是常量类型
, 因为不能添加属性
,如果定义在类中的{}
中,是成员变量
-
成员变量
中 除去基本数据类型
、NSString
,其他都是实例变量
(即可以添加属性
的成员变量
),实例变量
主要是判断是不是对象
五、元类 中为什么会有 类对象 的 类方法?
UIView
实际上也是一个对象,叫做类对象
,那么类对象的类又是什么呢?就是元类
。元类也是一个对象,它的类又是什么呢?根元类
。根元类也是一个对象,它的类又是什么呢?它自己
。
元类对象:OC
的类方法
是元类
存在的根本原因。因为元类对象
中存储着类对象调用的方法也就是类方法
。元类的定义和创建都是编译器自动完成的,无需人为干涉,而且大部分时候都是倾向于隐藏的。
六、实例对象、类对象、元类对象到底有什么区别呢?
-
实例对象:
实例对象
拷贝了实例所属的类的成员变量
,但不拷贝类定义的方法。当调用实例方法时,需要到类的方法列表里面去寻找方法的函数指针。 -
类对象:是一个功能完整的对象,它没有自己的实例变量。(这里要与类的成员变量区别开来,类的成员变量是属于实例对象的,而不是属于类对象的。类方法才是属于类对象自己的)。
类对象
中存储着成员变量
和实例方法列表
。 -
元类对象:
OC
的类方法
是元类存在的根本原因。因为元类对象
中存储着类对象调用的方法也就是类方法
。元类的定义和创建都是编译器自动完成的,无需人为干涉,而且大部分时候都是倾向于隐藏
的。
七、探究如下方法
- class_copyMethodList
- class_getInstanceMethod
- class_getClassMethod
- class_getMethodImplementation
首先定义一个类:NAPerson
@interface NAPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation NAPerson
- (void)sayHello {
NSLog(@"NAPerson say : Hello!!!");
}
+ (void)sayHappy {
NSLog(@"NAPerson say : Happy!!!");
}
@end
主方法调用如下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NAPerson *person = [NAPerson alloc];
Class pClass = object_getClass(person);
naObjc_copyMethodList(pClass);
naInstanceMethod_classToMetaclass(pClass);
naClassMethod_classToMetaclass(pClass);
naIMP_classToMetaclass(pClass);
}
return 0;
}
思考以下每个方法的打印信息:
获取类的方法列表
void naObjc_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));
NALog(@"Method, name: %@", key);
}
free(methods);
}
获取类的实例方法
void naInstanceMethod_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));
NALog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
获取类的类方法
void naClassMethod_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));
NALog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
获取方法的实现
void naIMP_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));
NALog(@"%s - %p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
以下是几个函数调用的打印结果:
WX20200916-173423.png
naObjc_copyMethodList函数 分析
在这个函数中,主要是获取NAPerson类中的方法列表,从实例方法
存储在类中,类方法
存储在元类中可以得知,NAPerson的方法列表打印结果只有sayHello
方法,如果NAPerson类定义了属性
该方法还会返回set/get
方法。
naInstanceMethod_classToMetaclass函数 分析
- 实例方法存在类对象中、类方法存在元类对象中
- 实例方法就是类对象的实例方法
- 类方法就是类元类对象的实例方法
naInstanceMethod_classToMetaclass函数 分析
- 获取类方法就是返回元类的实例方法
- 获取元类的类方法就是返回元类自己的实例方法
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
naIMP_classToMetaclass函数 分析
class_getMethodImplementation
主要是返回方法的具体实现,针对这个方法有如下官方说明
该函数在向类实例发送消息时会被调用,并返回一个
指向方法实现函数的指针
。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))
更快。返回的函数指针可能是一个指向runtime内部的函数
,而不一定是方法的实际实现
。如果类实例无法响应selector
,则返回的函数指针
将是运行时消息转发机制的一部分
.
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
//查找方法实现
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
//如果没有找到,则进行消息转发
if (!imp) {
return _objc_msgForward;
}
return imp;
}
lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
{
return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
}
OBJC_EXPORT void
_objc_msgForward(void /* id receiver, SEL sel, ... */ )
-
类
中能找到实例方法
的实现、元类
中能找到类方法
的实现所以imp1
和imp4
能打印出对应方法的实现地址 -
元类
中不能找到实例方法
的实现、类
中也找不到类方法
的实现,所以imp2
和imp3
打印出了相同的_objc_msgForward
方法实现地址
八、iskindOfClass & isMemberOfClass 的理解
对应的代码如下:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[NAPerson class] isKindOfClass:[NAPerson class]]; //
BOOL re4 = [(id)[NAPerson class] isMemberOfClass:[NAPerson class]]; //
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]]; //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re7 = [(id)[NAPerson alloc] isKindOfClass:[NAPerson class]]; //
BOOL re8 = [(id)[NAPerson alloc] isMemberOfClass:[NAPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
相关的源代码如下:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
需要注意的是调用的是类方法
还是实例方法
打印结果如下:1000 - 1111
理解isKindOfClass、isMemberOfClass实现的原理很重要,但更重要是能记住、并能在面试中灵活使用
在使用断点调试时我们会发现isMemberOfClass
的类方法和实例方法都会调用,但是isKindOfClass
的两个方法都不会调用。通过汇编调试
可发现isKindOfClass
的类方法和实例方法调用的都是objc_opt_isKindOfClass
方法:
objc_opt_isKindOfClass
方法源码如下:
// Calls [obj isKindOfClass]
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_opt_isKindOfClass
方法主要是因为在llvm中编译时对其进行了优化处理。
打印结果依然是:1000 - 1111
九、iOS中内省的几个方法?class方法和objc_getClass方法有什么区别?
- 什么是内省?
- 在计算机科学中,内省是指计算机程序在运行时
(Run time)
检查对象(Object)
类型的一种能力,通常也可以称作运行时类型检查。 - 不应该将内省和反射混淆。相对于内省,反射更进一步,是指计算机程序在运行时
(Run time)
可以访问、检测和修改它本身状态或行为的一种能力。
- iOS 中内省的几个方法?
-
isMemberOfClass:
//对象是否是某个类型的对象 -
isKindOfClass:
//对象是否是某个类型或某个类型子类的对象 -
isSubclassOfClass:
//某个对象是否是另一个类型的子类 -
isAncestorOfObject:
//某个类对象是否是另一个类型的父类 -
respondsToSelector:
//是否能响应某个方法 -
conformsToProtocol:
//是否遵循某个协议
-
class
方法和object_getClass
方法有什么区别?
- 实例
class
方法就直接返回object_getClass(self)
- 类
class
方法直接返回self
,而object_getClass(类对象)
,则返回元类
参考链接