类存在几份
由于类的信息在内存中只存在一份,所以类对象只有一份
objc_object与对象的关系
- 所有的对象都是以objc_object为模板继承过来的
- 所有对象都来自于NSObject,但是真正的底层是一个objc_object的结构体类型
所以objc_object与对象的关系是继承关系
什么是属性,成员变量,实例变量
- property(属性):通过@property开头定义,并带有下划线成员变量 + setter + getter方法的变量
- 成员变量:通过{}定义,且没有下划线的变量
- 实例变量:通过当前对象类型,具有实例化的变量,是一种特殊的成员变量,例如NSObject, UILab,UIButton等
实例变量与成员变量的区别
- 实例变量:成员变量中的对象变量就是实例变量,以实例对象实例化来的,是一种特殊的成员变量
- NSString是常量类型,不能添加属性,如果在{}中声明,是成员变量
- 成员变量中,除了基本数据类型和NSString,其他都是实例变量(能添加属性),对象类型的变量都是实例变量
元类中为什么会有类对象的类方法
下面定义几个方法,探索方法的归属问题
- 在LGPerson中定义一个实例方法和一个类方法
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayBye;
@end
@implementation LGPerson
- (void)sayHello
{
NSLog(@"LGPerson SayHelllo");
}
+ (void)sayBye
{
NSLog(@"LGPerson SayBye");
}
@end
- 定义以下几个runtime中的函数调用
- 1.获取类中的方法列表
void lgObjc_copyMethodList(Class pClass){
unsigned int count;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i = 0; i < count; i ++) {
Method method = methods[I];
NSString *selName = NSStringFromSelector(method_getName(method));
NSLog(@"方法名- %@",selName);
}
free(methods);
}
- 2.在类和元类中获取实例方法
void lgIntanceMethod_classToMetaClass(Class pClass){
const char * className = class_getName(pClass);
Class meatClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));//1
Method method2 = class_getInstanceMethod(meatClass, @selector(sayHello));//0
Method method3 = class_getInstanceMethod(pClass, @selector(sayBye));//0
Method method4 = class_getInstanceMethod(meatClass, @selector(sayBye));//1
NSLog(@"获取实例方法- %p %p %p %p",method1,method2,method3,method4);
}
- 3.在类和元类中获取类方法
void lgClassMethod_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(sayBye));
Method method4 = class_getClassMethod(metaClass, @selector(sayBye));
NSLog(@"获取类方法- %p %p %p %p",method1,method2,method3,method4);
}
- 4.在类和元类中获取IMP
void lgIMP_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(sayBye));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayBye));
NSLog(@"imp打印- %p %p %p %p");
}
以下是打印结果
下面分析以下其中的主要函数含义
-
class_copyMethodList
注释:获取传入class的method列表
-
class_getInstanceMethod
注释:获取传入class的Method,如果传入的cls是类(获取到的是实例方法),如果传入的cls是元类(获取到的就是类方法)
-
class_getClassMethod
注释:获取传入cls的Method,不管传入的cls是类还是元类,返回的都是类方法
why?查看其源码实现
通过源码实现可知,不管传入的cls是类还是元类,都是通过获取其元类的实例方法进行返回的
-
class_getMethodImplementation
注释:根据传入的cls和sel返回指定的IMP,注意注释中的一部分
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.
//翻译:
返回的函数指针可能是运行时内部的函数,而不是实际的方法实现。例如,如果类的实例不响应选择器,则返回的函数指针将是运行时消息转发机制的一部分。
如果类的实例不响应,将会进行消息转发,在源码中也可以看到相关信息
isKindOfClass和isMemberOfClass
- 面试题如下
NSLog(@"%d",[[NSObject class] isKindOfClass:[NSObject class]]);//
NSLog(@"%d",[[NSObject class] isMemberOfClass:[NSObject class]]);//
NSLog(@"%d",[[LGPerson class] isKindOfClass:[LGPerson class]]);//
NSLog(@"%d",[[LGPerson class] isMemberOfClass:[LGPerson class]]);//
NSLog(@"%d",[[NSObject alloc] isKindOfClass:[NSObject class]]);//
NSLog(@"%d",[[NSObject alloc] isMemberOfClass:[NSObject class]]);//
NSLog(@"%d",[[LGPerson alloc] isKindOfClass:[LGPerson class]]);//
NSLog(@"%d",[[LGPerson alloc] isMemberOfClass:[LGPerson class]]);//
打印结果如下
下面讲解一下源码
- isKindOfClass的类方法和实例方法
+ (BOOL)isKindOfClass:(Class)cls {
//取出self->ISA也就是元类,与传入的cls进行比较,递归元类的superclass一直比对
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
//取出[self class]也就是类,与传入的cls进行比对,递归类的superclass一直比对
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- isMemberOfClass的类方法和实例方法
+ (BOOL)isMemberOfClass:(Class)cls {
//取出self->ISA也就是元类,与cls进行比对
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
//取出[self class]也就是类,与cls进行比对
return [self class] == cls;
}
总结
- isKindOfClass:取出传入类的self->ISA,并进行递归查询superclass,与传入cls进行比对
- isMemberOfClass:取出传入类的self->ISA,不进行递归,直接与cls进行比较
- 注意点:NSObject元类的superclass是指向NSObject类的
然后再源码工程中进行断电调试,会发现isMemberOfClass会走源码工程中的方法,而isKindOfClass源码方法并没有走,此时通过查看汇编代码,发现调用isKindOfClass时会调用objc_opt_isKindOfClass方法
- 在源码中查找objc_opt_isKindOfClass
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
//取出传入对象的ISA
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
//然后用取出的cls的继承连(superClass)与传入的otherClass进行比对
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
走到这里的原因其实是在llvm编译时做了优化处理