您的位置:首页 > 移动开发 > Objective-C

<<Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法>>读书笔记之objc_msgSend

2016-10-04 23:13 681 查看
<span style="font-size:18px;">1.在Objective-C中,在对象上调用方法叫做"消息传递".</span>
<span style="font-size:18px;">2.在Objective-C中,如果向对象传递消息,那就会使用"动态绑定机制"来决定需要调用的方法.对象在收到消息后,究竟调用哪个方法则完全取决于运行时.</span>
<span style="font-size:18px;">给对象发送一条消息:id resultValue = [someObj messageName:parameter];</span>


在这里,someObj是receiver,messageName是selector.  receiver与selector合起来成为消息(message).编译器会将此方法转换成C语言函数:

id resultValue = objc_msgSend(someObj, @selector(messageName), parameter);

(1).objc_msgSend会根据接受者(receiver)和选择子(selector)的类型来调用适当的方法.

(2).objc_msgSend需要再接受者所属的类中搜索其方法列表(list of methods),若找到与选择子名称相符的方法,则跳转并实现.否则将继续寻找.若找不到与之相符的方法,则执行"消息转发操作".

(3).objc_msgSend会将匹配结果缓存在快速映射表中.

3.Objective-C利用了"尾调用优化"技术使得跳转到方法实现这一操作变得简单.

(1.)尾调用(Tail Call):就是指某个函数的最后一步是调用另一个函数。

function f(x){
return g(x);
}


以下两种情况,都不属于尾调用。

// 情况一
function f(x){
let y = g(x);
return y;
}

// 情况二
function f(x){
return g(x) + 1;
}


上面代码中,情况一是调用函数g之后,还有别的操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。

(2)尾调用优化

函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"(call
stack)。



function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();

// 等同于
function f() {
return g(3);
}
f();

// 等同于
g(3);


上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除
f() 的调用记录,只保留 g(3)的调用记录。

这就叫做"尾调用优化"(Tail call optimization),即只保留内层函数的调用记录。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。这就是"尾调用优化"的意义。

总结:

(1).消息由接受者,选择子和参数构成,给某对象发送消息也就相当于在该对象上调用方法.

(2).发给某对象的全部消息都要由"动态消息派发系统"来处理,该系统会查出对应的方法,并执行代码.

参考:http://www.ruanyifeng.com/blog/2015/04/tail-call.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐