Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不一样
Objective-C的动态性是由Runtime API来支撑的
Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
复制代码
在arm64架构以前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
复制代码
& 都是1才是1, 一个1,就是0
0000&0010 能够取出某一个特定位数值
掩码: 用来按位与(&)运算的
1<<0 左移0位 则为0b 0000 0001
1<<1 左移1位 则为0b 0000 0010
1<<2 左移2位 则为0b 0000 0400
1左移几位
复制代码
| 有1才是1, 一个1,就是1,2个0就是0
0000 | 0010 结果就是 0010
复制代码
~ 0000 1010 取反位1111 0101
复制代码
struct{
char tall : 1; //表示只占一位
char rich : 1;
char handsome: 1
} test // 0b0000 0111
复制代码
unioc{
char bits;
struct{
char tall : 1;
char rich : 1;
char handsome: 1
} test
}
复制代码
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,能够提升方法的查找速度
复制代码
缓存查找
objc-cache.mm
bucket_t * cache_t::find(cache_key_t k, id receiver)
复制代码
objc_msgSend(对象,sel_registerName(方法名字字符串))
// 消息接收者(receiver)
// 消息名称
// OC的方法调用: 消息机制,给方法调用者发送消息
内部执行分3大阶段
1. 消息发送
2. 动态方法解析
3. 消息转发
复制代码
1.经过方法名字去 类对象 方法缓存中查找,若是有则返回方法地址
2.若是没有缓存,则会遍历 方法列表查找
3.查到了方法返回,并添加到 缓存列表
4.若是没找到则会去父类缓存中查找,在去父类方法列表中查找,一层一层父类往上找
复制代码
1.若是 消息发送 未找到方法,则会进行动态方法解析
2.若是是 对象方法调用会 调用_class_resoveInstanceMethod()
若是是 类方法调用 调用 _class_resoveClassMethod()
3.+(BOOL)ResoveInstanceMethod:(SEL) sel{
// 获取其余方法
Method method = class_getInstanceMethod(self,@selector(test));
//动态添加方法
class_addMethod(self,
@selecetor(test),
method_getImplementation(method),
method_getTypeEndcoing(method));
return YES;
}
类方法同理
复制代码
动态添加方法 api
// 消息转发-
-(id)forwardTargetForSelector:(SEL) aSelector{
// 判断方法名字
if(aSelector == @selector(test)){
//转发给哪一个对象解决
return [NSPerson alloc]init];
}
}
// 消息转发- 方法签名,返回值类型,参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if(aSelector == @selector(testMethod))
{
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return nil;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if (anInvocation.selector == @selector(testMethod))
{
TestModelHelper1 *h1 = [[TestModelHelper1 alloc] init];
TestModelHelper2 *h2 = [[TestModelHelper2 alloc] init];
[anInvocation invokeWithTarget:h1];
[anInvocation invokeWithTarget:h2];
}
}
复制代码
[super message] 消息接受者仍是子类,方法寻找是从父类查找的
super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
struct objc_super2
SEL
复制代码
receiver是消息接收者
current_class是receiver的Class对象
复制代码
利用Runtime遍历全部的属性或者成员变量
利用KVC设值
复制代码
class_replaceMethod
method_exchangeImplementations
复制代码
动态建立一个类(参数:父类,类名,额外的内存空间)
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是否为元类
BOOL class_isMetaClass(Class cls)
获取父类
Class class_getSuperclass(Class cls)
复制代码
获取一个实例变量信息
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)
复制代码
获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
拷贝属性列表(最后须要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
复制代码
####12.22.4 Runtime API04 – 方法缓存
得到一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法实现相关操做
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
拷贝方法列表(最后须要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
动态添加方法
BOOL 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)
选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
用block做为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
复制代码
运行循环-在程序运行中循环作一些事情性能优化
应用范畴
定时器(Timer)、PerformSelector
GCD Async Main Queue
事件响应、手势识别、界面刷新
网络请求
AutoreleasePool
复制代码
RunLoop的基本做用
保持程序的持续运行
处理App中的各类事件(好比触摸事件、定时器事件等)
节省CPU资源,提升程序性能:该作事时作事,该休息时休息
......
复制代码
iOS中有2套API来访问和使用RunLoop
Foundation:NSRunLoop
Core Foundation:CFRunLoopRef
NSRunLoop和CFRunLoopRef都表明着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装
CFRunLoopRef是开源的
https://opensource.apple.com/tarballs/CF/
复制代码
每条线程都有惟一的一个与之对应的RunLoop对象
RunLoop保存在一个全局的Dictionary里,线程做为key,RunLoop做为value
线程刚建立时并无RunLoop对象,RunLoop会在第一次获取它时建立
RunLoop会在线程结束时销毁
主线程的RunLoop已经自动获取(建立),子线程默认没有开启RunLoop
复制代码
Foundation
[NSRunLoop currentRunLoop]; // 得到当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 得到主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); // 得到当前线程的RunLoop对象
CFRunLoopGetMain(); // 得到主线程的RunLoop对象
复制代码
CFRunLoopModeRef表明RunLoop的运行模式
一个RunLoop包含若干个Mode,每一个Mode又包含若干个Source0/Source1/Timer/Observer
RunLoop启动时只能选择其中一个Mode,做为currentMode
若是须要切换Mode,只能退出当前Loop,再从新选择一个Mode进入
- 不一样组的Source0/Source1/Timer/Observer能分隔开来,互不影响
若是Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
复制代码
常见的2种Mode
kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,一般主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其余 Mode 影响
复制代码
添加Observer监听RunLoop的全部状态bash
控制线程生命周期(线程保活)
解决NSTimer在滑动时中止工做的问题
监控应用卡顿
性能优化
复制代码
runloop 若是没有任何 source/source0 timer/observer 就会退出
复制代码