运行时-二
2015-11-18 22:29
288 查看
从一道面试题说开去
有一个Person类继承于NSObject,它有一个子类Student。现在在Student里写下如下代码,求输出结果
Student *stu = [[Student alloc] init]; NSLog(@"%@ %@ %@ %@",[self class],[super class],[self superClass],[super superClass]);
class
class方法的作用:返回【方法调用者】的类型 比如[UITableViewCell class]superClass方法的作用:返回【方法调用者】的父类类型,
super的含义:子类可以借助
super关键字去调用父类的方法 比如if (self = [super init]).所以[super class]意思是self借助它父类的class方法,调用者依然是self,返回的是子类
self的含义:方法调用者.
所以无论self还是super调用,方法调用者都是self
运行时
用终端来到文件夹,里面有个OC文件比如main.m,输入clang -rewrite-objc main.m,可以重写成C++语言代码,有十万多行,因为要支撑起整个项目确实要这么多,即使它只有一个main官方文档的介绍:
In Objective-C, messages aren’t bound to method implementations until runtime. The compiler converts a message expression, [receiver message] into a call on a messaging function, objc_msgSend. This function takes the receiver and the name of the method mentioned in the message—that is, the method selector—as its two principal parameters: objc_msgSend(receiver, selector)
所有方法都是转成发消息,比如上面一行代码,receiver调用message方法,会转换成给receiver发送消息”message”
我们在写OC程序的时候用的都是运行时方法
可以通过运行时来查看我们代码的C语言底层实现。这里以[super description]为例详细解释
在main程序里写如下代码,目的是研究super到底是怎么实现的
@interface Person : NSObject @end @implementation Person - (NSString *)description{ return [super description]; } @end int main(int argc, const char * argv[]) { @autoreleasepool { } return 0; }
然后按照上面的方法转成C++代码,搜索”description” (小细节:消息都是带双引号的,带上双引号能缩小范围),得到如下代码A
return ((NSString *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("description"));
上面官方文档可知:给谁发消息,谁就是消息接受者receiver,谁就是方法调用者,因此只要看是给谁发消息就知道是谁调用了方法
很多小括号是类型强转,可以去掉,而大括号是个结构体,比如
struct Date{ int year; int month; } struct Date d = {2015,10};
把结构体换成一个参数,剩下的也是一个参数,再把强转的小括号去掉,就简化成如下
id temp1 = {self, class_getSuperclass(objc_getClass("Person"))}; id temp2 = sel_registerName("description") return objc_msgSendSuper(temp1, temp2);
代码A其实就是[super description]的底层实现。现在能看懂了,
super关键字调用的是objc_msgSendSuper函数,而一般调用objc_msgSend这个函数(与super比较很简单,略过不表,我们说super)。而它的第一个参数temp1就是方法调用者receiver,第二个参数是方法description
点击objc_msgSendSuper这个方法进去看一下说明,它的两个参数是struct objc_super *super和SEL op
重点是第一个参数(对应为temp1),再点进去看说明,是下面这些
struct objc_super { /// Specifies an instance of a class. __unsafe_unretained id receiver; /// Specifies the particular superclass of the instance to message. #if !defined(__cplusplus) && !__OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained Class class; #else __unsafe_unretained Class super_class; #endif /* super_class is the first class to search */ };
这个结构体乍看有三个参数,实际上是两个:后面那个是二选一(如果不是C++并且不是ObjC2,选一,否则选二,现在是选二)
__unsafe_unretained id receiver; __unsafe_unretained Class super_class;
可以看出这个结构体的第一个参数就是receiver,对应的上面的temp1的第一个参数是self,OK,真相大白,消息接受者也就是方法调用者就是self
再看看结构体的第二个参数(id)class_getSuperclass(objc_getClass(“Person”)),class_getSuperclass意思是取得父类,那么这句代码意思就是objc_getClass(“NSObject”)。于是temp1参数现在就是id temp1 = {self,objc_getClass(“NSObject”)}
于是,代码A的意思就是person这个类,去它的父类NSObject找到description方法给自己调用
相关文章推荐
- 第一次spring冲刺第7天
- mysqladmin flush-hosts
- iOS开发之应用程序
- 热更新lua语法
- QT: :QWheelEvent
- IntentServices源码分析
- Tomcat java.lang.OutOfMemoryError: PermGen space及其解决方法
- .Net 扩展方法集合.
- final修饰符---final类
- Android ProgressBar的案例
- VC中使用XML
- 【转载】linux 技巧:使用 screen 管理你的远程会话
- 浮点数(有限浮点数、无限循环浮点数)的精确表达
- db2离线备份、在线备份和回复
- HDU 5402 Travelling Salesman Problem(组合数学+构造)
- c语言:使用main函数的参数,实现一个整数计算器
- Hadoop HDFS原理
- android蓝牙ble4.0开发
- Linux MMC/SD/SDIO体系结构
- iOS小技巧12-苹果地图和高德地图的关系