[TOC]
总结isa走位图 本节揭晓 为什么要有元类??
总结思考 isa-的走位图 a: 对象->类->元类 b: 继承链LGPerson *p; LGTeacher *t ; t:p 继承只来自于类 类有继承关系 对象没有 c: LGTeacher -> LGPerson -> NSObject -> nil 无中生有 d: 元类是否也有isa -> 根元类isa -> 根元类(NSObject的元类) e: 元类是否也有继承链 元类 类 继承 LGPerson的元类 -> 根元类 -> NSObject -> nil 本节揭晓 为什么要有元类??
类的结构 内存平移 calss_data_bit 偏移0x20 32字节 坑点 没在objc源码工程中打印调试
objc_setProperty 和 内存平移赋值 两种形式
// objc_object VS objc_class objc_class : objc_object // person VS NSObject person本质是objc_object结构体 ,NSObject是objc_class结构体 // id 是 objc_object * 指针 // class 是objc_class * 指针
WWDC关于runtime的变化
类的结构 (developer.apple.com/videos/play…)
类的内存ro数据
firstSubclass = nil 没有值 继续操作,操作一下子类LGTeacher.class 这时 firstSubclass = LGTeacher 有了 类的加载 懒加载
此时给类添加成员变量和方法 继续操作
成员变量在ro里面 打印 ro( )方法,得到class_ro_t类型的数据
继续打印成员变量 ivars 可以看到count=3 有三个成员变量 hobby _age _name 下面探索 成员变量 和 属性 的区别 再下面探索 类方法
成员变量和属性以及编码
成员变量、 实例变量、 属性
@interface LGPerson : NSObject
{
// STRING int double float char bool
NSString *hobby; // 字符串 成员变量
int a;
NSObject *objc; // 结构体 实例变量
}
@property (nonatomic, copy) NSString *nickName;//属性
@property (atomic, copy) NSString *acnickName;
@property (nonatomic) NSString *nnickName;
@property (atomic) NSString *anickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
@end
复制代码
成员变量、 实例变量、 属性 区别: 属性 = 待下划线的成员变量 + setter + getter方法 实例变量 : 特殊的成员变量(类的实例化)
clang 操作之后 属性被优化掉 生成对应的 _nickName 成员变量,与成员变量的区别是 属性还会生成相应的set、get方法
同时通过对比发现 @property (nonatomic, copy) NSString *nickName; @property (nonatomic, strong) NSString *name; 这两个属性的set方法不一样
为什么会有 objc_setProperty 和 内存平移赋值 两种形式??
还有这些编码的意思
{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
@16@0:8
1: @ : id
2: 16 : 所占用的内存
3: @ : id 参数
4: 0 : 从0号位置开始
5: : : SEL
6: 8 : 从8号位置开始
{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
v24@0:8@16
1: v void
2: 24 参数所占用内存
3: @ id 参数
4: 0 : 从0号位置开始
5: : : SEL
6: 8 : 从8号位置开始
7: @ id 传入的参数
8: 16 : 从16号位置开始
复制代码
例如:ivar_getTypeEncoding()
进入Documentation搜索
Type Encodings (developer.apple.com/library/arc…)
另外可以通过以下方法打印:
#pragma mark - 各种类型编码
void lgTypes(void){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
}Person;
NSLog(@"struct --> %s",@encode(Person));
typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));
int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}
复制代码
至于成员变量和属性的区别 可以通过下面方法打印获取
#import <objc/runtime.h>
#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif
void lgObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
LGLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
//获取属性值
LGLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}
复制代码
调用方法
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyIvar_copyProperies(pClass);
class_copyIvarList:hobby
class_copyIvarList:a
class_copyIvarList:objc
class_copyIvarList:_nickName
class_copyIvarList:_acnickName
class_copyIvarList:_nnickName
class_copyIvarList:_anickName
class_copyIvarList:_name
class_copyIvarList:_aname
class_copyProperiesList:nickName
class_copyProperiesList:acnickName
class_copyProperiesList:nnickName
class_copyProperiesList:anickName
class_copyProperiesList:name
class_copyProperiesList:aname
复制代码
setter方法的底层原理上
编译时代码已经确定了 不好绑定 llvm查看源码 在 运行时 进行 sel -> imp(objc_setProperty)
setter方法的底层原理下
##为什么会有 objc_setProperty 和 内存平移赋值 两种形式?? objc_setProperty 通过查阅llvm 发现一些列调用过程 copy的情况才会调用 objc_setProperty
可以继续进行 objc_getProperty 原理拓展
为何objc_getProperty 要查llvm 而不是看objc源码 runtime源码中objc_getProperty 和setName 没有关联 setName(imp) 编译时去绑定imp 如果setName 编译时 没有和imp绑定 ,则 运行时要先调用相应方法 有可能定位方法错误(machoView查看)
类方法的存储
(# 类的内存ro数据) 继续最初的class_rw_t的遍历
可以发现类方法 + (void)sayNB;
没有打印出来,类方法也有对应的实现,但就是没有打印出来 通过machoview可以看到类方法是有的
以上打印发现 - (void)saySomeThing;
实例方法、对象方法在 类里面(节省内存) (+ -) 方法在底层C C++统称函数,如果+-方法同名如何处理
- (void)saySomeThing;
+ (void)saySomeThing;
复制代码
所以 元类由此诞生 类方法存放在元类中 6 66 66666 验证一下
下面探索不用llvm,用另一种方式验证
类方法的存储的API方式解析
通过runtime提供的api来验证
#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import "LGTeacher.h"
#import <objc/runtime.h>
#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif
void lgObjc_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));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
void lgInstanceMethod_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));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
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));
// - (void)sayHello;
// + (void)sayHappy;
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
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));// 0
// sel -> imp 方法的查找流程 imp_farw
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
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 {
// LGTeacher *teacher = [LGTeacher alloc];
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
NSLog(@"*************");
lgObjc_copyMethodList(metaClass);
lgInstanceMethod_classToMetaclass(pClass);
lgClassMethod_classToMetaclass(pClass);
lgIMP_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
Method, name: sayHello
Method, name: obj
Method, name: setObj:
Method, name: .cxx_destruct
Method, name: name
Method, name: setName:
2021-06-25 18:22:41.130738+0800 002-类方法归属分析[31216:2542102] *************
Method, name: sayHappy
lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158
lgClassMethod_classToMetaclass - 0x0-0x0-0x100008158-0x100008158
2021-06-25 18:22:45.041969+0800 002-类方法归属分析[31216:2542102] 0x100003ad0-0x7fff6d5e3580-0x7fff6d5e3580-0x100003b10
2021-06-25 18:22:45.042185+0800 002-类方法归属分析[31216:2542102] lgIMP_classToMetaclass
2021-06-25 18:22:46.077799+0800 002-类方法归属分析[31216:2542102] Hello, World!
复制代码
打印结果分析 lgInstanceMethod_classToMetaclass - 0x1000081c0-0x0-0x0-0x100008158 实例方法!!! pClass sayHello方法有 metaClass sayHello方法没有 pClass sayHappy方法没有 metaClass sayHappy方法有(实例方法) lgClassMethod_classToMetaclass - 0x0-0x0-0x100008158-0x100008158 类方法!!! pClass sayHello方法没有 metaClass sayHello方法没有 pClass sayHappy方法有 metaClass sayHappy方法有(上面lgInstanceMethod_classToMetaclass的打印已经证明了类方法在元类中以对象方法存储,这里为何在元类中还是能打印出类方法???)
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
复制代码
在底层 没有类方法 只有对象方法
lgIMP_classToMetaclass 0x100003ad0-0x7fff6d5e3580-0x7fff6d5e3580-0x100003b10 都有值 为何 元类的sayHello方法 imp有值? 为何 类的sayHappy方法imp有值? 查源码 _objc_msgForward 指针的原因
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
// sel -> imp 方法的查找流程 imp_farw
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
复制代码
isKindOfClass面试题
isKindOfClass补充
查看源码
isKindOfClass
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
//BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //1
//BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; //0
cls元类 -> cls元类的父类
判断是否相等
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
//BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //1
//BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //1
复制代码
isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
//BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //0
//BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; //0
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
//BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //1
//BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; //1
复制代码
打开汇编调试 发现isKindOfClass调用的不像源码的一样 调用的是objc_opt_isKindOfClass
待探索。。。