0
点赞
收藏
分享

微信扫一扫

关于OC中self和super的笔记

深夜瞎琢磨 2021-09-29 阅读 29
日记本

运行以下代码查看log输出,为什么NSLog的输出都Animal ?

// Person.h 文件
#import "Animal.h"

@interface Person : Animal

- (void)test;

@end


// Person.m 文件
#import "Person.h"

@implementation Person

- (void)test {
    
    NSLog(@"super---%@", [super class]);
    NSLog(@"self---%@", [self class]);
}

@end

//// Log
[19476:618282] super---Person
[19476:618282] self---Person

1. 对象初始化的两种方式

对象初始化有两种方式:[class new] 与 [[class alloc] init]。对于后者,有分配和初始化的过程,alloc 从应用程序的虚拟地址空间上为该对象分配足够的内存,并且将新对象的引用计数加1、将对象的成员变量初始为零,init 会做真正的初使化工作,为对象的实例变量赋予合理有用的值

查看相关源码:

// new
+ new { 
    id newObject = (*_alloc)((Class)self, 0); 
    Class metaClass = self->isa; 
    if (class_getVersion(metaClass) > 1) 
    return [newObject init]; 
    else 
    return newObject; 
} 
 
// 而 alloc、init 
+ alloc { 
    return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());  
} 
- init { 
    return self; 
} 

发现[class new]默认调用 alloc与init方法,多了更多的局限性。而[class alloc] init] 会更方便, 当然[class alloc] init] 的设计也是由于历史的原因。

2. init方法中的self = [super init];

常见的重写一个类的init方法中,都会有如下写法:

- (instancetype)init {
    if (self = [super init]) {
        // Custom initialization
    }
    return self;
}
  • self是什么
    self代表着当前方法的调用者。在对象方法中,self代表当前"实例对象";在静态方法中,self则代表"类对象"。

  • super是什么
    self 和 super 是oc 提供的两个保留字,不同的是:self是类的隐藏的参数变量,指向调用当前方法的对象;super并不是隐藏的参数,它只是一个编译器指示符。(另一个隐藏参数是_cmd,代表当前类方法的selector)

  • [super init]做了什么

在一个类的方法reposition中,编译如下调用方法时,

// Class  A
- (void)reposition {  
     ...  
     [self setOrigin:someX];  
     ...  
}

编译器会将调用过程转换为:

objc_msgSend(id self,SEL _cmd, ...); //self ->实例对象

此时 self 指代实例对象,方法从对象对应 类结构的方法列表 中开始寻找,如果找不到,延继承链往 父类中寻找 。同样如果 reposition 是类方法, self 指代 A 类对象。

// Class  A
- (void)reposition {  
     ...  
     [super setOrigin:someX];  
     ...  
}

编译器会将调用过程转换为:

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

其中,第一个参数是个objc_super的结构体,

struct objc_super {
   id receiver;
   Class superClass;
};

当开始编译代码 [super setOrigin:someX :someY]; 时,则需要做这几个事:

  1. 构建 objc_super 的结构体,其成员变量 receiver 就是当前实例对象(self) 。而成员变量 superClass 就是指类A的 superClass。
  2. 调用 objc_msgSendSuper 的方法,将这个结构体和 setOrigin: 的 sel 传递过去。

这也解释了文章,开头为什么[super class]和[self class]输出一致。
当 发送 class 消息 时不管是 self 还是 super 其消息主体依然是 self ,也就是说 self 和 super 指向的 是同一个对象。只是 查找方法的位置 区别,一个从本类,一个从本类的超类。一般情况下class方法 只有在根类 NSObject 中定义,极少情况有子类重写 class 方法,如果重写可能会不一样。
super只是个编译器符号,它可以替换成 [self class],只不过它方法是从 self 的超类开始查找。

3. 为什么要 if (self = [super init])

这样符合OC继承类初始化规范流程,[super init]递归的去self的super中调用init,直到调用根类 NSObject 中的init。即,根据各级super的init来初始化内存区域的属性,再逐级返回内存指针,直到A类中得到内存指针,赋值给self 参数。若中途初始化参数失败,则if中self为nil,可免接下来无用的初始化。

4. 总结

  1. 面向对象过程中子类继承父类,就拥有了父类所有的属性和方法,一个完整的对象的初始化包括子类和父类初始化。
  2. 子类 [alloc init] 后,首先这里只有一个实例对象实体self,没有所谓的父类对象实体super。初始化过程中,子类、父类属性和方法初始化都属于子类对象的一部分,super的指针赋给self这一说法是错的,其实全部指的是该对象的初始位置。

参考文章:
https://www.cnblogs.com/damontang/p/4034890.html
https://blog.csdn.net/lin1109221208/article/details/108724965
https://opensource.apple.com/source/objc4/objc4-750.1/runtime/objc-runtime-new.h.auto.html
https://opensource.apple.com/source/objc4/objc4-750.1/runtime/objc-private.h.auto.html

举报

相关推荐

0 条评论