一万小时定律的遗憾之Objective-C的self与super

原文:一万小时定律的遗憾之Objective-C的self与super

Malcolm T.Gladwell说:“人们眼中的天才之所以卓越非凡,并非天资超人一等,而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。”

对于一万小时定律,我是深信不疑的,然而真的尝试做着的时候,才发现一万小时,对于天资愚钝的我还远远不够。

学习Objective-C已近两年,每天的工作学习的平均时间也控制在十个小时以上,渐渐地一直以为自己对其机制及语法有了一定认识,然而看到别人对两个编程中最常用到关键字self、super的理解的时候,实在羞愧,深感自己对平日使用的东西了解还太少太少。

super.jpg

正文

当然,这一切都要从一个Model和一个网上的笔试题说起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1、一个Model
#import <Foundation/Foundation.h>
@interface EVNHelper : NSObject
- (instancetype)init;
@end
#import "EVNHelper.h"
@implementation EVNHelper
- (instancetype)init
{
self = [super init];// ???
if (self)
{
// other init code
}
return self;
}
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2、 一个笔试题
- (instancetype)init
{
self = [super init];
if (self)
{
NSLog(@"self className is %@", NSStringFromClass([self class]));
NSLog(@"super className is %@", NSStringFromClass([super class]));
NSLog(@"self superclass className is %@", NSStringFromClass([self superclass]));
NSLog(@"super superclass className is %@", NSStringFromClass([super superclass]));
}
return self;
}
@end
输出:
2016-12-24 16:04:32.600 Demo[6009:1006834] self className is EVNHepler
2016-12-24 16:04:32.601 Demo[6009:1006834] super className is EVNHepler
2016-12-24 16:04:32.601 Demo[6009:1006834] self superclass className is NSObject
2016-12-24 16:04:32.602 Demo[6009:1006834] super superclass className is NSObject

对应的问题也来了:

1、子类初始化为什么要写self = [super init],意义?
2、为什么[self class]和 [super class];[self superclass]和[super superclass]输出相同?

可到底self和super是什么呢?(由于对runtime懂得太少,怕误人子弟,暂不扯底层代码,只说自己能看到的)

  • self
    之前我的理解,self是对象指针,指向当前类的实例对象。其实这种理解是不准确的,因为在静态方法self 依然可以使用(如下代码),这怎么解释呢!
1
2
3
4
5
6
7
8
+ (void)helperMethodA
{
[self helperMethodB];
}
+ (void)helperMethodB
{
NSLog(@"asdfasdfasd");
}

带着这份好奇,最终我在NSObject协议中找到了self,原来self是返回instancetype类型的代理方法,但又有些奇怪,我们在自定义子类的时候并没有实现这个方法,我认为self的功能,OC已经帮我们实现,只是我们看不到而已。这样就明确了,self最终返回的结果就是instancetype类型的东西,它是动态类型,最终运行时才会确定,实例方法返回实例类型、静态方法返回的是Class。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <objc/objc.h>
#include <objc/NSObjCRuntime.h>
@class NSString, NSMethodSignature, NSInvocation;
@protocol NSObject
- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
@property (readonly) Class superclass;
......
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self;
......
@end

  • super
    在Foundation框架中,super是找不到的,该关键字应该是比self更为奇特的东西。虽然找不到super,但我们可以看到结构体objc_super,在官方解释中我们能看到一些端倪。
    objc_super:The compiler generates an objc_super data structure when it encounters the super keyword as the receiver of a message. It specifies the class definition of the particular superclass that should be messaged.

大致意思就是,当遇到super关键字时,编译器会生成一个objc_super结构体,作为消息的接收者,objc_super结构体使得接收消息的父类的定义被明确化。
所以,super的含义应该是一种编译指令,它的作用用来给父类发送消息,并返回消息响应的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <objc/objc.h>
#include <objc/runtime.h>
#pragma GCC system_header
#ifndef OBJC_SUPER
#define OBJC_SUPER
/// Specifies the superclass of an instance.
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__
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
};

知悉了这两点,上面的两个问题也就迎刃而解了。

解答:

问题一:self = [super init]是面向对象思想的一种体现,意义就是,利用父类的init方法为子类初始化父类的公有属性。
问题二:理解这个先要明确alloc和init的区别,alloc为对象开辟内存,init是对象初始化,所以,[super init] 是初始化在子类上的,super发出消息的主体对象是子类,和self是同一个对象。 class方法和self类似是NSObject协议中的代理方法,我认为class的功能,OC也已经帮我们实现在每个子类中,只是我们看不到而已,所以不管是super还是self发出的class消息,执行的时候,都在子类中,返回的都是子类。

参考: