Clang 拾遗之objc_designated_initializer

简介:

动因

前几天在Review代码时,发现了NS_DESIGNATED_INITIALIZER使用得并不正确。
我写了一篇博客(Playing with attributes 系列)。专门研究这个问题。
这里摘录一下。

objc_designated_initializer

使用方法

@interface MyObject:NSObject
- (instancetype)init __attribute__((objc_designated_initializer));
@end
AI 代码解读

在iOS中也可以写成

- (instancetype)init NS_DESIGNATED_INITIALIZER;
AI 代码解读

该属性可以指定类的初始化方法。指定初识化方法并不是对使用者。而是对内部的现实。譬如,下面这种情况

实例讲解

@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
AI 代码解读

解释一下:

  • 如果是DESIGNATED_INITIALIZER的初始化方法,就必须调用父类的DESIGNATED_INITIALIZER方法。

    [1]没有warning,因为NSObjectinit也是DESIGNATED_INITIALIZER。[4]也同样正确,父类的initMyObject是DESIGNATED_INITIALIZER。所以[6]就不正确了,因为initMyObject4同样是DESIGNATED_INITIALIZER。

  • 如果不是DESIGNATED_INITIALIZER的初始化方法,但是该类拥有DESIGNATED_INITIALIZER初始化方法,那么:

  • 必须调用该类的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。

  • 不可以调用父类的任何初始化方法。

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

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

    [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;
}
AI 代码解读

居然没有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,但却漏了继承过来的情况。

当然这只是我的猜测。

目录
打赏
0
0
0
0
2
分享
相关文章
|
11月前
|
Xposed模块 -- Hook函数参数
Xposed模块 -- Hook函数参数
136 0
Boost(MSVC编译)+使用信号槽
天天看QT的信号槽,突然发现boost中也有信号槽,所以想试试boost的信号槽,尝试一下。需要先对boost进行编译,之后我会使用信号槽作为演示代码。
192 0
UNITY_INITIALIZE_OUTPUT宏
UNITY_INITIALIZE_OUTPUT宏 UNITY_INITIALIZE_OUTPUT(type,name) –此宏用于将给定类型的名称变量初始化为零。在使用旧版标准所写的Shader时,经常会报错“Try adding UNITY_INITIALIZE_OUTPUT(Input,o); like this in your vertfunction.”之类的错误,加上这句就不会报错了。
在MSBuild.exe中使用条件编译(Conditional Compile)
参数的写法:"/p:DefineConstants=MY_PREPROC_FLAG YET_ANOTHER_FLAG" 注意,在使用 MSBuild 时,会出现C#项目中设置的输出路径无效的问题,这是要在MSBuild参数中加入:   参考:http://www.
660 0
开源项目推荐:POCO C++ Libraries大全,含MFC的编译及使用
开源项目推荐:POCO C++ Libraries大全,含MFC的编译及使用
770 0
开源项目推荐:POCO C++ Libraries大全,含MFC的编译及使用
解决办法:GTK_OBJECT、GTK_SIGNAL_FUNC未声明
解决办法:GTK_OBJECT、GTK_SIGNAL_FUNC未声明
100 0
Android应用开发—重载fragment构造函数导致的lint errors
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/voidreturn/article/details/78302771 背景...
1170 0
CMake指定交叉编译指南:从编译器设置到验证 (CMake Cross-compilation Guide: From Compiler Setup to Verification)
CMake指定交叉编译指南:从编译器设置到验证 (CMake Cross-compilation Guide: From Compiler Setup to Verification)
2438 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等