iOS self super关键字

在了解到runtime的知识后,有人会问self和super是怎么来的,有什么作用,当然想都不想就说self是指自身的实例类,super表示父类。这样回答很浅,也不是太准确,结合runtime的知识会理解的更透彻。毕竟runtime是装逼利器。

看之前先说下oc的消息转发,比如一个对象object调用方法[object message],如果理解成调用message方法不太准确,oc会把它转化成objc_msgSend(object, message) ,如果消息的接收者object能够找到对应的selector,那么就相当于直接执行了接收者这个对象的特定方法;否则,消息要么被转发,或是临时向接收者动态添加这个selector对应的实现内容,要么就干脆玩完崩溃掉。oc在编译期间只是确定发送message调用的信息,具体如何实现要看运行时的了。

self

我们先来看self,这里用ClangViewController来演示,分别用self和nsobject调用方法,这里主要是为了看方法转换成c++代码的样子。

oc的m文件转换c++代码用的是命令行的clang命令

1 进入终端程序,cd到m文件的路径

2 输入clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ClangViewController.m 然后回车(这里命令很长,只要是头文件包含了UIKit框架,网上找的这个方案,ClangViewController.m也可以换成其他的m文件

3 在同一级的路径下会有一个ClangViewController.cpp文件,打开就是对应的c++代码,代码非常多,我们只需要看关键的地方。

来到cpp文件,全局搜索@implementation,就可以来到方法对应的代码,我们可以看到这里和oc的代码不一样,大部分都是运行时的代码(关于runtime大家可以网上搜索,资料很多)。

我们先看第62985行[ob description],被转换的代码((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)ob, sel_registerName("description")),objc_msgSend是个消息发送,第一个参数表示发送给哪个对象(id)ob,第二个表示要响应哪个方法sel_registerName("description"),所有的方法调用都是这么来的,通过runtime的源码可以看到他的定义

在这里我们看到是第一个参数self,他就相当于我们写方法定义的参数一样,然后我们再看62981行_I_ClangViewController_viewDidLoad的方法,这里多了self和_cmd的参数,viewDidLoad方法也是一样的转发机制,self就是消息转发中的参数,传递过来的实例对象,只是在oc代码中没有显示出来,

再看62986行[self clang],被转换成((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("clang")),这里的self指的就是_I_ClangViewController_viewDidLoad后面参数里的self,clang的方法实现里也有个self参数接收,这样我们在每个实例方法里都可以用self这个关键字。

super

我们继续看ClangViewController.cpp文件,第62982行是[super viewDidLoad]转化的代码((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ClangViewController"))}, sel_registerName("viewDidLoad")),super其实转换成objc_msgSendSuper后面的都是他的参数,我们在runtime源码里可以看到它的定义

这里先看上面的英文解释,英文不好直接谷歌翻译

  *用简单的返回值向类的实例的超类发送消息。

  * @param super指向一个\ c objc_super数据结构的指针。 通过标识的值

  *消息发送到的上下文,包括要接收消息的类的实例以及开始搜索方法实现的超类。

  * @param op SEL类型的指针。 传递将处理消息的方法的选择器。

  * @param ...

  *包含方法参数的变量参数列表。

  * @return由\ e op标识的方法的返回值。

  * @see objc_msgSend

可以看到第一个参数object_super是一个结构体,里面包括接收消息的实例,搜索方法的父类,第二个参数表示调用的方法,我们可以进入object_super这个结构体看看

很显然receiver就是实例对象,super_class用于搜索父类里的方法,这样我们就可以对应代码一点一点分析((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ClangViewController"))}, sel_registerName("viewDidLoad"))

(__rw_objc_super)就是定义这个结构体,大括号里的都是,第一个(id)self就是receiver,(id)class_getSuperclass(objc_getClass("ClangViewController"))是super_class用于寻找父类里的方法,sel_registerName("viewDidLoad")是objc_msgSendSuper的第二个参数,调用的方法名字。

如果是[super init]的话,super会调用其父类的init,以此类推,直到找到根类NSObject中的init。然后根类中的init负责初始化内存区域,添加一些必要的属性,返回内存指针,延着继承链,指针从上到下进行传递,同时在不同的子类中可以向内存添加必要的属性。最后直到我们当前类中把内存地址赋值给self参数。当然,如果调用[super init]失败的话,通过判断self来决定是否执行子类的初始化操作。

这样是不是理解的更好一点,下面我们再来看一个网上经常说的[self class]和[super class],我们先在runtime源码中看下class方法的调用

NSStringFromClass([self class])和NSStringFromClass([super class])是否相同,关键在于方法调用时传递的对象是否一样,也就是super返回的对象是否为self,还记得objc_msgSendSuper的英文解释么,第一个参数objc_super结构体的第一个参数receiver是接收消息的实例,super被转化成c++代码时receiver接收的是self,[super class]执行的也就是[self class],所以两个返回的class是相同的,不要被表面的假象所迷惑。

你可能感兴趣的:(iOS self super关键字)