一.isa
isa的理解
- 在arm64架构之前, isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
- 从arm64架构开始,对isa进行了优化,变成了一个共用体( union )结构,还使用位域来存储更多的信息
使用64位(8个字节)存储了大量信息
源码---右方数字表示占用的位数
union isa_ t
{
Class cls;
uintptr_ ,t bits;
struct (
uintptr_ t nonpointer : 1;
uintptr_t has_assoc: : 1;
uintptr_t has_cxX_ dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_ rc : 1;
uintptr_t extra_rc : 19;
);
};
注意:shiftcls保存着类对象的地址值,占用33bit
位运算---设值与取值:
取值用按位与& (0b0000 0001-取第八位数值)
设值用按位与&(0b1111 1110-设第八位为0) + 按位或|(0b0000 0001-设第八位为1):需要确保其他值不变,只修改想设置的值
(按位非 ~)
二.class结构
cache缓存
Class内部结构中有个方法缓存( cache_t ) , 用散列表来缓存曾经调用过的方法,可以提高方法的查找速度(空间换时间)
SEL --函数(方法)名
imp --指向函数的指针(函数地址)
用下面一张图大概概括class的结构
三.objc_msgSend 消息转发
OC的方法调用:消息机制,给方法调用者发送消息
objc_msgsend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
两个关键实现方法:+resolveInstanceMethod:/+resolveClassMethod:
在OC中调用某个方法,当第一步,消息发送阶段未找到此方法时,会进入第二步---动态方法解析
//动态添加实例方法
- (void)other
{
NSLog(@"%s",__ func__ );
}
+ ( BOOL)resolveInstanceMethod: (SEL )sel
{
if(sel == @selector(test)) f
// 获取其他方法
Method method = class_ getInstanceMethod(self, @selector(other));
//动态添加test方法的突現
class_addMethod(self, sel,
method_getImplementation(method) ,
method_getTypeEncoding(method));
//返回YES代表有幼恋添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- 消息转发
当方法调用的第一步,与第二步都没有调用成功的时候。会进入第三步--消息转发,执行方法 forwardingTargetForSelector
//当进入第三步时,会调用 MJCat 的test方法
- (id)forwardingTargetForSelector: (SEL)aSelector{
if (aSelector == @selector(test)) f
return [[MJCat alloc] init];
}
return [super forwardingTargetForSelector :aSelector];
}
扩展
提醒编译器不要自动生成setter和getter的实现、不要自动生成成员变量
@dynamic age;
四.[ self class]与[super class]
一.[ self class]的底层实现
- 1.objc_msgSend消息接收者仍然是子类对象
所以super class与self class 本质是一样的
-(Class) class{
return object_ getClass(self );
}
- 2.从父类开始查找方法的实现
二.[self superclass]的底层实现
superclass方法的返回值,也是取决于消息接收(调用)者是谁
- (Class) superclass //获取调用者的父类
{
return class_getSuperclass(object_getClass(self)); //获取self对象的父类方法
}
因此 [self superclass]与[super superclass]的本质一样
同一个类中,下面两个方法的打印结果是一样的
NSLog(@"[self class] = %@", [self class]);
NSLog(@"'[ super class]= %@"[super class]);
NSLog (@"[ self superclass] = %@", [self superclass]) ;
NSLog (@"[ super superclass] = %@", [super superclass]) ;
五.isKindOfClass和isMemberOfClass
实例方法
-(BOOL)isKindOfClass:(Class)aClass;// 判断左边实例的类对象或其父类的类对象是否跟右边类对象相等
-(BOOL)isMemberOfClass:(Class)aClass; // 判断左边实例的类对象是否跟右边类对象相等
类方法
+(BOOL)isKindOfClass:(Class)aClass;//左边类的类对象或父类的类对象(即元类)是否跟右边类对象相等
+(BOOL)isMemberOfClass:(Class)aClass;//左边类的类对象(即元类)是否跟右边类对象相等
六.面试题
一.讲一下OC的消息机制
- OC中的方法调用其实都是转成了objc msgSend函数的调用,给receiver (方法调用者)发送了一条消息 ( selector方法名)
2.objc_ msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发
二.什么是Runtime?平时项目中有用到过吗?
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的, Runtime是一 套C语言的API ,封装了很多动态性相关的函数
使用:
1.类相关: 利用关联对象,给分类添加属性(成员变量)
2.成员变量:遍历类的所有成员变量(修改textfield的占位文字颜色(修改成员变量值)、字典转模型(YYmodel的核心)、自动归档解档)
3.方法相关:
1.方法交换(hook(钩子)方法,最常用的应用是防止数组、字典等越界崩溃).
2.利用消息发送机制进行非常规的方法调用 objc_msgSend
方法交换实例:
+ (void)swizzleInstanceMethod2:(SEL)originalSel with:(SEL)swizzledSel {
Method originalMethod = class_getInstanceMethod(self, originalSel);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSel);
NSLog(@"给当前分类添加原类的方法originalSelector");
BOOL didAddMethod =
class_addMethod(self,
originalSel,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
NSLog(@"originalSelector方法在原类中不存在,已经添加成功,用下面的方法替换其实现");
class_replaceMethod(self,
swizzledSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
NSLog(@"如果原类中已存在originalSelector的话,那么添加失败而已,返回NO");
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
方法交换代码解析
方法交换应用场景//统计用户进入各个控制器的次数
- Runtime中类相关的API
//动态创建-一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_ _t extraBytes)
//注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
//销毁一个类
void objc_disposeClassPair(Class cls)
//获取isa指向的Class
Class object_getClass(id obj)
//设置isa指向的Class
Class object_setClass(id obj, Class cls)
//判断一个OC对象是否为Class
BOOL object_isClass(id obj)
//判断一个Class是否为元类
BO0L class_isMetaClass(Class cls)
//获取父类
Class class_getSuperclass(Class cls)
- Runtime中成员变量相关的API
//获取一个实例变量
Ivar class_getInstanceVariable(Class cls, const char *name)
//拷贝实例变量列表(最后需要调用free释放)
Ivar *Class_copyIvarList(Class cls, unsigned int *outCount)
//设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
//动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
//获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
- Runtime中方法相关的API
//获得一个实例方法、类方法
Method class_ getInstanceMethod(Class cls, SEL name)
Method class_ _getClassMethod(Class cls, SEL name)
//方法实现相关操作
IMP class_ getMethodImplementation(Class cls, SEL name)
IMP method_ set Imp lementation (Method m, IMP imp)
void method_ exchangeImp lementat ions (Method m1, Method m2)
//拷贝方法列表(最后需要凋用free釋放)
Method *Class_ copyMethodList(Class cls, unsigned int *outCount)
//动态添加方法
B00L class_ addMethod(Class cls, SEL name, IMP imp, const char *types)
//动态替換方法
IMP class_ replaceMethod(Class cls, SEL name, IMP imp, const char *types)
//获取方法的相关信息(帯有copy的需要凋用free去釋放)
SEL method_ getName (Method m)
IMP method_ getImplementation(Method m)
const char *method_getTypeEncoding (Method m)
unsigned int method_getNumberOfArguments (Method m)
char *method_copyReturnType (Method m)
char *method_copyArgumentType(Method m, unsigned int index)