复习一下 iOS 基础 (1)

之前和人聊天, 发现自己各种基础知识很多都记得不是很清楚了, 所以在这里做一个回顾和总结

Runtime

runtime, ios 运行时
我们可以通过 runtime 做很多的事情

  • 替换系统/类库的方法
  • 增加属性
  • 获取 类的 ivar 成员 property 属性 method 方法 protocol 协议 等等
  • 动态的创建类
  • ...等等

一些概念

@selector 和 SEL 方法名(编译前)/方法编号(编译后) -- 方法映射表 -> Method -> IMP 函数实现
Method 是一种结构体 里面封装了 SEL IMP 和 返回值参数类型

Paste_Image.png

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结构体指针

Paste_Image.png

上图实线是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它具体是如何发送消息:

  1. 首先根据receiver对象的isa指针获取它对应的class
  2. 优先在class的cache查找message方法,如果找不到,再到methodLists查找
  3. 如果没有在class找到,再到super_class查找
  4. 一旦找到message这个方法,就执行它实现的IMP。

6.修改属性

Ivar *ivar = class_copyIvarList([self.xiaoming class], &count);
const char *varName = ivar_getName(var);
object_setIvar(self.xiaoMing, var, @"20");

消息转发机制

  1. Method Resolution // 第一步 让你自己动态添加方法 + resolveInstanceMethod:或+ resolveClassMethod:
  2. Fast Forwarding // 第二步 返回一个新的对象去执行该方法 forwardingTargetForSelector
  3. Normal Forwarding // 第三步 于上一步类似, 区别是需要我们自己使用 NSInvocation 来完成消息转发 methodSignatureForSelector和forwardInvocation
Paste_Image.png

概念

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源运行当中随时调用这个函数。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,162评论 0 7
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,278评论 0 6
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 743评论 0 1
  • 虚构电影故事,根据江歌事件改编。 引子 日本东京法庭,尽管有451万的请愿者签名要求判处死刑,杀人者陈峰还是只判了...
    江湖觅道阅读 1,887评论 0 0