Playing with __attributes__ (四)

objc_designated_initializer

使用方法

@interface MyObject:NSObject
- (instancetype)init __attribute__((objc_designated_initializer));
@end

在iOS中也能够写成segmentfault

- (instancetype)init NS_DESIGNATED_INITIALIZER;

该属性能够指定类的初始化方法。指定初识化方法并非对使用者。而是对内部的现实。譬如,下面这种状况设计

实例讲解

@interface MyObject:NSObject
- (instancetype)initMyObject NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObjectNonDesignated;
@end

@implementation MyObject        //[8] 产生warning
- (instancetype)initMyObject{
    self = [super init];//[1] 没有warning
    return self;
}

- (instancetype)initMyObjectNonDesignated
{
    self = [self initMyObject];
    return self;
}
@end

@interface DerivedObject:MyObject
- (instancetype)initMyObject2;
- (instancetype)initMyObject3;
- (instancetype)initMyObject4 NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObject5;
- (instancetype)initMyObject6 NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObject7;
@end

@implementation DerivedObject   //[9] 产生warning
- (instancetype)initMyObject2{
    self = [super init];//[2] 产生warning
    return self;
}

- (instancetype)initMyObject3{
    self = [self initMyObjectNonDesignated];//[3] 没有warning
    return self;
}

- (instancetype)initMyObject4{
    self = [super initMyObject];//[4] 没有warning
    return self;
}

- (instancetype)initMyObject5{
    self = [self init];//[5] 没有warning
    return self;
}

- (instancetype)initMyObject6{
    self = [self initMyObject4];//[6] 产生warning
    return self;
}

- (instancetype)initMyObject7{
    self = [self initMyObject];//[7] 没有warning
    return self;
}
@end

解释一下:code

  • 若是是DESIGNATED_INITIALIZER的初始化方法,就必须调用父类的DESIGNATED_INITIALIZER方法。继承

[1]没有warning,由于NSObjectinit也是DESIGNATED_INITIALIZER。[4]也一样正确,父类的initMyObject是DESIGNATED_INITIALIZER。因此[6]就不正确了,由于initMyObject4一样是DESIGNATED_INITIALIZER。get

  • 若是不是DESIGNATED_INITIALIZER的初始化方法,可是该类拥有DESIGNATED_INITIALIZER初始化方法,那么:it

  1. 必须调用该类的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。io

  2. 不能够调用父类的任何初始化方法。循环

[2]调用的父类的方法 不正确,改为[5]这样就对了 [3]调用的该类的方法(从父类继承过来的),正确
[7]也调用的该类的方法(从父类继承过来,但会产生其余问题,见下面解释)方法

  • 若是一个类拥有DESIGNATED_INITIALIZER初始化方法,那它必须覆盖实现父类定义的DESIGNATED_INITIALIZER初始化方法。im

[8] [9]都是由于没有覆盖实现父类的DESIGNATED_INITIALIZER方法

注:对于非DESIGNATED_INITIALIZER,llvm把它称为Convenience intializer

总述

这个attribute的目的实际上是在初始化类的实例时,不管调用关系如何复杂,必须调用一次该类的Designated intializer(能够有多个),对于 Designated intializer,必须调用父类的Designated intializer。对于父类的父类这个规则亦然,对Designated intializer的调用一直要到根类。

对于上述例子,调用触发顺序应该为:

DerivedObject Convenience intializer-> 若干次其余DerivedObject Convenience intializer -> DerivedObject Designated intializer -> MyObject Designated intializer -> NSObject Designated intializer

其余

其实llvm还漏了一些细节,看上述代码:

- (instancetype)initMyObject3{
    self = [self initMyObjectNonDesignated];//[3] 没有warning,若是改为super 就有warning
    return self;
}

竟然没有Warning!这样的话在类会跳过该类的Designated intializer。Holy High!从上述的解释来看,对Convenience intializer,llvm是没有要求全部的Convenience intializer必须调用Designated intializer,但这个attribute的设计思路要求终归要调用一次该类的Designated intializer

对于上述状况,我能想到的解释就是llvm还没智能到能分析较为复杂的状况。如不考虑继承。一个类的Convenience intializer总会有一个会调用Designated intializer,否则就会有循环调用的可能,因此基于这个假设,llvm没有对Convenience intializer调用Convenience intializer的状况抛出Warning,但却漏了继承过来的Convenience intializer状况。

固然这只是个人猜想。

原做写于segmentfault 连接

相关文章
相关标签/搜索