之前和人聊天, 发现自己各种基础知识很多都记得不是很清楚了, 所以在这里做一个回顾和总结
Runtime
runtime, ios 运行时
我们可以通过 runtime 做很多的事情
- 替换系统/类库的方法
- 增加属性
- 获取 类的 ivar 成员 property 属性 method 方法 protocol 协议 等等
- 动态的创建类
- ...等等
一些概念
@selector 和 SEL 方法名(编译前)/方法编号(编译后) -- 方法映射表 -> Method -> IMP 函数实现
Method 是一种结构体 里面封装了 SEL IMP 和 返回值参数类型
Ivar:成员属性的意思
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员,根据isa指针就可以顺藤摸瓜找到对象所属的类。
Class其实就是一个objc_class结构体指针
上图实线是super_class指针,虚线是isa指针。有几个关键点需要解释以下:
- Root class (class)其实就是NSObject,NSObject是没有超类的,所以Root class(class)的superclass指向nil。
- 每个Class都有一个isa指针指向唯一的Meta class
- Root class(meta)的superclass指向Root class(class),也就是NSObject,形成一个回路。
- 每个Meta class的isa指针都指向Root class (meta)。
self表示当前这个类的对象,而super是一个编译器标示符,和self指向同一个消息接受者
_cmd表示当前调用方法,其实它就是一个方法选择器SEL。一般用于判断方法名或在Associated Objects中唯一标识键名,后面在Associated Objects会讲到
具体用法
1. 方法交换
Method m1 = class_getClassMethod(self, @selector(m1:));
Method _m1 = class_getClassMethod(self, @selector(_m1:));
method_exchangeImplementations(m1, _m1);
Method class_getInstanceMethod(Class cls, SEL name) // 这个是获取类方法
原理是 把 method 映射 到 IMP 的映射关系交换
2. 添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
原理是动态添加映射关系, 给出 SEL 和对应的 IMP(其实就是函数), 兵器给出 types(返回值和参数类型)
这里需要注意的就是我们要注意是否添加成功, 如果失败, 正明存在原有方法
3.挂载属性
id objc_getAssociatedObject(id object, const void *key)
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
4.获取对象相关信息
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) // 获取成员
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) // 获取属性
Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount) //获取协议
Method *class_copyMethodList(Class cls, unsigned int *outCount) // 获取方法
........ 等等
5.发送消息
id objc_msgSend(id self, SEL op, ...)
三个参数简单理解就是 对象 函数名 参数
objc_msgSend它具体是如何发送消息:
- 首先根据receiver对象的isa指针获取它对应的class
- 优先在class的cache查找message方法,如果找不到,再到methodLists查找
- 如果没有在class找到,再到super_class查找
- 一旦找到message这个方法,就执行它实现的IMP。
6.修改属性
Ivar *ivar = class_copyIvarList([self.xiaoming class], &count);
const char *varName = ivar_getName(var);
object_setIvar(self.xiaoMing, var, @"20");
消息转发机制
- Method Resolution // 第一步 让你自己动态添加方法 + resolveInstanceMethod:或+ resolveClassMethod:
- Fast Forwarding // 第二步 返回一个新的对象去执行该方法 forwardingTargetForSelector
- Normal Forwarding // 第三步 于上一步类似, 区别是需要我们自己使用 NSInvocation 来完成消息转发 methodSignatureForSelector和forwardInvocation
概念
NSInvocation;用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数
NSMethodSignature:签名:再创建NSMethodSignature的时候,必须传递一个签名对象,签名对象的作用:用于获取参数的个数和方法的返回值
NSInvocation 中的方法 必须与 NSMethodSignature 中的一样
需要注意的是 NSInvocation 中的参数需要从 2 开始设置, 因为 0 和 1 分别是 target 和 SEL
GCD 的一些用法
dispatch_barrier_async
同一并行队列中, dispatch_barrier_async 会等待之前的所有任务全部执行完, 卡住队列, 之后执行自己的任务, 任务结束队列恢复正常
dispatch_apply
异步的 for 循环
dispatch_suspend和dispatch_resume
暂停和开始队列
dispatch_semaphore_signal
一个信号量, 里面有值 semaphore
dispatch_semaphore_signal(semaphore) 会把 semaphore + 1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 会等待信号量, 如果 semaphore == 0, 则会卡住等待信号唤醒, 如果 semaphore > 0 则 semaphore - 1
dispatch_queue_set_specific和dispatch_get_specific
作用类似objc_setAssociatedObject跟objc_getAssociatedObject, 我们可以绑定对象, 可以使用它来判断当前队列是否是当前使用的队列, 防止死锁
一些 GCD 的注意点
GCD 的并行队列 会使用多条线程, sync 方法 会在当前线程, async 会在分线程
GCD中的一些概念
dispatch_source 分派源
简单就是向 dispatch_source 发送事件, 触发 block, dispatch_source 需要设定执行的 dispatch_queue,
其实我更希望把它理解成对 dispatch_source 的监听, 当事件发生的时候, 执行任务.
我们可以把它当做一个异步回调来使用,
根据不同的 type, dispatch_source 可以被主动调用, 可以监听一些系统事件或者文件变化等等, 也可以设置为 timer
* DISPATCH_SOURCE_TYPE_DATA_ADD: application defined data
* DISPATCH_SOURCE_TYPE_DATA_OR: application defined data
* DISPATCH_SOURCE_TYPE_MACH_SEND: dispatch_source_mach_send_flags_t
* DISPATCH_SOURCE_TYPE_MACH_RECV: n/a
* DISPATCH_SOURCE_TYPE_MEMORYPRESSURE dispatch_source_memorypressure_flags_t
* DISPATCH_SOURCE_TYPE_PROC: dispatch_source_proc_flags_t
* DISPATCH_SOURCE_TYPE_READ: estimated bytes available to read
* DISPATCH_SOURCE_TYPE_SIGNAL: number of signals delivered since
* the last handler invocation
* DISPATCH_SOURCE_TYPE_TIMER: number of times the timer has fired
* since the last handler invocation
* DISPATCH_SOURCE_TYPE_VNODE: dispatch_source_vnode_flags_t
* DISPATCH_SOURCE_TYPE_WRITE: estimated buffer space available
DISPATCH_SOURCE 常用方法
dispatch_suspend(queue) //挂起队列
dispatch_resume(source) //分派源创建时默认处于暂停状态,在分派源分派处理程序之前必须先恢复
dispatch_source_merge_data //向分派源发送事件,需要注意的是,不可以传递0值(事件不会被触发),同样也不可以传递负数。
dispatch_source_set_event_handler //设置响应分派源事件的block,在分派源指定的队列上运行
dispatch_source_get_data //得到分派源的数据
uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第二个参数
unsigned long dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第三个参数
void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件处理--即不再调用block。如果调用dispatch_suspend只是暂停dispatch源。
long dispatch_source_testcancel(dispatch_source_t source); //检测是否dispatch源被取消,如果返回非0值则表明dispatch源已经被取消
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler); //dispatch源取消时调用的block,一般用于关闭文件或socket等,释放相关资源
void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t registration_handler); //可用于设置dispatch源启动时调用block,调用完成后即释放这个block。也可在dispatch源运行当中随时调用这个函数。