0%

objc继承中的init方法

在创建一个objc对象时,alloc负责分配内存,init负责对对象进行初始化。也就是说,init其实就是其他语言中的构造函数。

但是如果需要在构造函数中调用父类与子类同名的对象方法时,objc和C++两种语言输出的结果却截然不同。

先看C++:

#include <iostream>
using namespace std;
class A{
public:
    A(){
        log();
    };
    void log(){
        cout<< "A:";
        cout<< this <<endl;
    }
};


class B:public A{
    
public:
    B(){
        log();
    }
    
    void log(){
        cout<< "B:";
        cout<< this <<endl;
    }
};

int main(int argc, const char * argv[]) {
    B *b = new B;
    delete b;
    return 0;
}

输出结果为:
A:0x100200540
B:0x100200540

可见调用顺序为: 父类构造函数->基类同名方法->子类构造函数->子类同名方法。

再看objc:

#import <Foundation/Foundation.h>

@interface A : NSObject 

@end

@implementation A
- (instancetype)init
{
    if (self = [super init]) {
        [self log];
    }
    return self;
}

- (void)log
{
    NSLog(@"A: %@",self);
}
@end

@interface B : A

@end
@implementation B
- (instancetype)init
{
    if (self = [super init]) {
        [self log];
    }
    return self;
}

- (void)log
{
    NSLog(@"B: %@",self);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        B *b = [[B alloc]init];
    }
    return 0;
}

执行结果为:

 ocinit[29764:9300639] B: <B: 0x100300480>
 ocinit[29764:9300639] B: <B: 0x100300480>
 

通过断点,确认执行顺序为: 父类构造函数->子类同名方法->子类构造函数->子类同名方法。

原因猜测:

C++中对象的内存布局中会保存当前对象所有函数的地址,调用时会首先在本类的内存布局中查找要调用的函数,若未找到,则会在父类中继续查找。 所以父类的构造函数中调用的是自身的同名方法。

而objc的实例对象(instacne objec)中都有一个isa指针,指向它的类对象(class object),类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等。 在进行alloc操作时,就进行了isa指针的初始化,所以在父类的构造方法中调用的其实也是子类类对象中储存的方法, 即子类的同名方法。