从iOS点语法引发的一番思考

aluo 7年前
   <p>当我们去点一个属性的时候,我们知道其实是调用了属性的setter或者getter方法。那么,用点调用一个方法会发生什么?</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b9b2a27ac0ca785322a775173bed6e68.png"></p>    <p>这时候系统并不会崩溃,而只是报一个警告(Property access result unused)。</p>    <p>现在我改成用中括号正常调用方法,可是由于粗心没有在.m中写方法的实现,会发生什么呢?</p>    <p>OC是一门动态编程语言。</p>    <p>调用saySomething这个方法。如果这个方法只是在.h中声明了,而没有在.m中实现。那么我们编译程序不会出现问题(Build Success),只有当运行程序的时候,才会直接崩溃(unrecognized selector sent to instance 0x60000000e8d0)</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/afcf2910b66a68b93c6763f80d85dada.png"></p>    <p>这是因为在编译阶段,编译器并不知道saySomething要执行那段代码,这个时候[people saySomething]会转换为objc_msgSend(people, "someThing"),就是说给people以selector的形式发送someThing这个消息。当真正运行的时候,才会去people的内存中寻找someThing的地址,这时候找不到才会造成崩溃。</p>    <p>那么,有没有办法说可以不在.m中实现somtThing,而又不让程序崩溃呢?</p>    <p>有!所谓 动态语言,就是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。</p>    <p>想要程序不崩溃,我们有三次机会去拯救它。这个就是 <strong>消息转发机制的三个步骤</strong> :</p>    <p>第一步:动态方法解析</p>    <p>下图可以看到,我没有实现saySomething的方法,程序运行成功。实际上是调了doSomething这个方法。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a96e368c68be68692bff6e2afc58813d.png"></p>    <p>我们这里的saySomething是对象方法,如果是类方法的话,对应的是resolveClassMethod:</p>    <p>第二步:备用接收者</p>    <p>如果我们浪费了第一次机会不用,还有第二次机会拯救工程:把这个消息转给别人,让别人替我处理!</p>    <p>下图可以看到,我没有实现run方法,而是交给了_dog去处理这个消息。在Dog.m中,我实现了的run方法被调用了!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1ccf07f7a8ac25c7cce9bd085245c625.png"></p>    <p>第三步:完整转发消息</p>    <p>如果我们连第二次机会都浪费了,那么还有最后一次机会补救。在完整转发消息这一步中,我们把消息封装到NSInvocation对象中,并把它发给一个能够响应消息的对象。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/afe94078b8ba09795dadbe9bbb08fac1.png"></p>    <p>观察消息转发机制的后两步,其实都已经不是people在处理消息了,而是交给了别人。但是在外面看,我们是调用了[people run] 和 [people eat] 。通过这种方式可以将本来对象A要做的事交给B或C来做。</p>    <p> </p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000008047643</p>    <p> </p>