iOS self和super的异同

“大师,今天小师妹从一本古籍中发现一些秘术,并以此问了我一个问题,可我并不知道,这下要在小师妹那里丢人了..”bash

“少侠莫慌,先且把题目示于老夫。”架构

“题目是这样”app

在一个继承于NSObject的类中,调用[self class][super class],结果分别是什么?iphone

@interface Person : NSObject

@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"Person self: %@", [self class]);
        NSLog(@"Person super: %@", [super class]);
    }
    return self;
}

@end

复制代码

“少侠,看好了,答案是这样。”ide

Person self: Person
Person super: Person
复制代码

“大师,为何是这样呢,[super class]不该该打印出NSObject吗?”ui

“少侠,且听老夫细细道来。”spa

使用clang编译器将上面使用的OC文件,编译成C++文件,来看一下源码。3d

可使用命令:指针

  • clang -rewrite-objc filename.m
  • xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc filename.m -o output.cpp

第二种方法过滤了平台以及CPU架构,因此编译成C++文件以后,代码量会更少一些。code

在编译以后的C++文件中,找到init方法。

static instancetype _I_Person_init(Person * self, SEL _cmd) {
    self = ((Person *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("init"));
    if (self) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_x9_w2_m5ffn49j8y3805_0sq6bm0000gn_T_Person_29eaed_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_x9_w2_m5ffn49j8y3805_0sq6bm0000gn_T_Person_29eaed_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("class")));
    }
    return self;
}
复制代码

在这里面,找到了两个重要的方法。

源码方法

[super class][self class]的不一样,就在于objc_msgSendobjc_msgSendSuper的不一样。

接下来,咱们打开runtime的源码,查看一下这两个方法作了什么。runtime的源码在这里下载

下面来看一下这两个方法:

id objc_msgSend(id self, SEL op, ...)

self
A pointer that points to the instance of the class that is to receive the message.
用来接收消息的当前类的实例

op
The selector of the method that handles the message.
当前方法的selector

...
A variable argument list containing the arguments to the method.
复制代码

self是当前类的一个实例,用来接收方法。 op是方法,在此例中是class方法。

id objc_msgSendSuper(struct objc_super *super, SEL op, ...);

Sends a message with a simple return value to the superclass of an instance of a class.

super
A pointer to an  objc_super data structure. Pass values identifying the
context the message was sent to, including the instance of the class that is to receive the message and the superclass at which to start searching for the method implementation.
一个指向objc_super的指针

op
A pointer of type SEL. Pass the selector of the method that will handle the message.
当前方法的selector

...
A variable argument list containing the arguments to the method.
复制代码

objc_super是结构体,结构体结构以下:

struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull 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 _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
复制代码

简化一下就是:

struct objc_super {
   id receiver;
   Class super_class;
};
复制代码

objc_super结构体里面,包含了当前类的对象做为消息接收者,super_class表明了从父类开始寻找方法。

至此,selfsuper这两个的区别已经明了:

  • [self msg]从当前类中开始寻找方法,寻找不到父类中寻找。
  • [super msg]从父类中开始寻找方法。
  • [self msg][super msg]中的消息接收者,均为当前类的对象。

在此例中,[receiver class]方法,在NSObject类中,因此从Person中寻找和从NSObject中寻找的结果同样,消息的接收者又都是Person的实例,因此产生了开头的结果。

另从编译以后的代码中,能够看到方法都自带两个隐藏的参数,一个是self,一个是cmd。

默认参数

self表明当前类的对象 cmd表明当前方法的selector

cmd能够有如下做用:

  • 打印当前方法
- (void)test
{
    NSLog(@"call: %@", NSStringFromSelector(_cmd));
}
复制代码

打印出:

call: test
复制代码
  • 在给category绑定属性时,能够用_cmd来做为惟一的key,由于selector是一个惟一的字符串
- (CustomNavigationControllerDelegate *)customDelegate
{
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setCustomDelegate:(CustomNavigationControllerDelegate *)customDelegate
{
    objc_setAssociatedObject(self, @selector(customDelegate), customDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

复制代码

为何getter方法里面用_cmd,可是setter方法里用@selector(customDelegate),由于_cmd表明的是当前方法,在getter里面时和在setter里面时,是不同的。做为key,两个方法里同样。使用上面的方法就确保了两个地方的key同样。

“以上就是题目的来龙去脉,以及默认参数的使用方法。”

“大师一番话让小生豁然开朗,多谢大师。”

“少侠快去找小师妹去罢。”

相关文章
相关标签/搜索