说到本质, 在 OC 中那就自然而然的想到 runtime 和 C 语言函数. 方法的调用很多人都知道是消息转发机制来实现的, 那究竟我们调用一个类的方法的时候, OC 底层干了哪些事呢, 还有我们 controller 中的 self 或者 UIView 中的 self 又是从哪里来的呢, 是系统自己给的吗? 看完我的文章你就会恍然大悟.
1. 首先创建一个 Person 类,.h 文件中写一个方法
2. 然后在 viewController 文件中引入 Person 类, 然后创建 Person 类对象, 然后实现方法, 这个相信大家都会.
3. 这个时候我们就来在 Person.m 文件中用底层实现这个方法. 我们进入 Person.m 文件中会看到报一个黄色的警告,
意思就是我们之前定义的方法没有实现, 那我们就用一个类方法来替代实现
看方法名字我们就知道他的意思, 就是给类添加一个对象方法, 还有添加类方法, 可以自己去尝试看看, 其中参数 sel 意思就是方法, 也就是我们未实现的 eatWithFood 方法了.
4. 接下来我们利用 runtime 来为其动态添加一个方法, 首先我们需要在. m 文件中导入头文件 #import<objc/runtime.h>, 然后给类添加一个方法, 就要用到 class_addMethod 这个方法. 看图:
这个方法需要导入四个参数, 分别是 cls,SEL name,IMP,types, 那这四个参数是什么意思呢.
在这里插一句, 为什么系统知道我调用一个方法比如 [self eat], 然后就会执行 eat 中的方法呢, 而不是其他的方法呢, 这个就好比一本书的目录, 我们想看哪一课的内容, 怎么办, 先去目录中找到这一课的名字, 然后根据名字找到对应的页码, 然后再翻到那一页去看, 好了, 关键是课名, 页码, 内容, 课名对应的就是 SEL, 页码就是 IMP, 内容就是具体的方法实现. IMP 本质就是函数指针, 指向该方法名在堆中的地址, 然后进到这个地址里面去读取内容. 然后这个时候会有人问这个 types 是一个 const char* 是一个 c 语言, 为啥写成这样. 不急, 稍后再说. 接下来我们先打印一下结果看看.
为什么打印的是这个, 应该是 "这是吃了汉堡包" 才对, 为什么呢?
5. 其实我们忽略了 eat 函数实现的时候省略了两个参数, 那就是 id self ,SEL _cmd, 这两个参数默认是隐藏的.
这个时候你再运行打印一下, 看看结果
, 结果就出来了, 是不是很神奇. 那这两个参数是怎么来的呢?
6. 下面我们就来解释这两个参数的由来, 那就要从 [personeatWithFood:@"汉堡包"]; 这个代码说起, 我们都知道, 在 OC 中调用方法, 实际就是利用消息转发机制, 那我们就把这个方法用底层的消息转发机制来实现. 首先我们先要在 buildSettting 中设置一下,
, 将 Enable Strict Checking of objc_msgSend Calls 设置为 NO, 这个是严格检测, 如果不设置为 NO 的话, 使用 objc_msgSend 这个函数会报错. 那接下来我们就来看看怎么转变的. 首先在 ViewController.m 中导入头文件 #import<objc/message.h>, 然后写代码
, 我们首先看看这个方法调用需要什么参数, 一个 id 类型的参数, 一个 SEL 类型的, 后面还可以跟多个其他参数, 那我们就来实现 [personeatWithFood:@"汉堡包"] 这个方法.
这个时候我们运行, 发现照样能得到跟之前一样的输出,"这是吃了汉堡包", 那就说明用消息转发机制实现了我们 [personeatWithFood:@"汉堡包"] 这个方法, 他们是等同的. 然后我们再来前后对比一下, 消息转发机制的参数和 Person 类里面 eat 函数的参数,
是不是有点恍然大悟的感觉, 原来 id self ,SEL _cmd 参数是消息转发方法里面带过来的, 现在是不是知道我们平时使用的 self 或者 self. 属性 中的 self 是怎么来的吗, 都是传递过来, 只不过系统隐藏了而已.
7. 细心的同学会发现这个时候我上面的 types 类型的写法怎么又变了, 之前是 "v@", 现在是 "V@:@", 其实这个类型是根据 void eat(id self,SEL _cmd,NSString *food){} 这个函数来的, 其中 "v" 代表的是返回值是 void, "@" 代表的是一个 OC 对象的参数,":" 代表的是 SEL 方法, 所以在一起就是 "V@:@", 其实这个 types 没有多大用处, 你写一个 "" 空的 c 字符串在那也不影响正常输出, 但是为了规范一般这样写, 有兴趣的可以自己试下是不是任意写都可以.
8. 好了, 看完上面是不是恍然大悟了, 关注我的文章, 会为你带来更多简单易懂的 OC 底层知识.
来源: http://www.jianshu.com/p/07a74e113945