【问题标题】:iOS Designated Initializers : Using NS_DESIGNATED_INITIALIZERiOS 指定初始化器:使用 NS_DESIGNATED_INITIALIZER
【发布时间】:2014-10-03 19:08:44
【问题描述】:

我们在 XCode 6 中引入了这个新宏:NS_DESIGNATED_INITIALIZER

我在网上搜索过,但实际上找不到任何关于如何使用它的好文档。

在语法上,我们可以像这样使用它:

- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

但是用这个宏标记一个初始化器有什么好处,以及我们在使用这个宏时应该注意什么?

我主要对这个宏的用例感兴趣。任何链接/文档将不胜感激。

【问题讨论】:

    标签: ios objective-c xcode initialization xcode6


    【解决方案1】:

    NS_DESIGNATED_INITIALIZER 的使用在http://useyourloaf.com/blog/2014/08/19/xcode-6-objective-c-modernization.html 中有很好的解释:

    指定的初始化器保证对象被完全初始化 通过向超类发送初始化消息。这 实现细节对类的用户来说变得很重要,当 他们将其子类化。指定初始化器的详细规则:

    • 指定的初始化程序必须调用(通过 super)指定的 超类的初始化器。其中 NSObject 是 this 的超类 只是[超级初始化]。
    • 任何便利初始化程序都必须调用另一个 类中的初始化器 - 最终导致指定的 初始化器。
    • 具有指定初始化器的类必须实现所有 超类的指定初始值设定项。

    例如,如果你的界面是

    @interface MyClass : NSObject
    @property(copy, nonatomic) NSString *name;
    -(instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
    -(instancetype)init;
    @end
    

    然后编译器检查(方便)初始化器init 是否调用 (指定的)初始化程序initWithName:,所以这会导致警告:

    -(instancetype)init
    {
        self = [super init];
        return self;
    }
    

    这样就可以了:

    -(instancetype)init
    {
        self = [self initWithName:@""];
        return self;
    }
    

    Swift 中,关于指定初始化器和便利初始化器的规则更加严格, 如果你混合使用 Objective-C 和 Swift 代码,标记指定的 Objective-C 初始化器有助于编译器执行规则。

    例如,这个 Swift 子类会导致编译器错误:

    class SwClass: MyClass {
        var foo : String
        init(foo : String) {
            self.foo = foo
            super.init()
        }
    }
    

    这样就可以了:

    class SwClass: MyClass {
        var foo : String
        init(foo : String) {
            self.foo = foo
            super.init(name: "")
        }
    }
    

    【讨论】:

    • Objective-C 中的概念并没有经过深思熟虑,因为initWithName:@"" 在子类化时并不总是有意义。相反,通常从超类重写的指定初始化程序应该抛出异常,说要使用子类中的指定初始化程序。
    • 要添加到 Martin 的答案,您可能希望完全禁止用户调用默认初始化程序,在这种情况下,您可以使用以下命令:- (instancetype)init NS_UNAVAILABLE;
    【解决方案2】:

    我最常用的方法:

    @interface Person : NSObject
    
    - (nullable instancetype)initWithName:(nonnull NSString *)name NS_DESIGNATED_INITIALIZER;
    - (nullable instancetype)init NS_UNAVAILABLE;
    
    @property (nonatomic, nonnull) NSString *name;
    
    @end
    

    及实施

    @implementation Person
    
    - (instancetype)initWithName:(NSString *)name
    {
        self = [super init];
        if (self) {
            self.name = name;
        }
        return self;
    }
    
    @end
    

    在这种情况下,您不应覆盖超类方法的NS_DESIGNATED_INITIALIZER(在这种情况下为NSObjectinit:) - 我们使用NS_UNAVAILABLE 将此方法标记为不需要。或者您可以覆盖它以使用默认参数调用您指定的初始化程序。

    【讨论】:

      【解决方案3】:

      指定初始化器定义了我们在子类化时如何构造初始化器;它们是您班级的“规范初始化程序”。无论您调用超类链中的哪个指定初始化程序,它都保证是可靠的,并且总是从最远的祖先到最远的后代。

      指定的初始化器没有定义创建对象时应该使用的初始化器。在https://blog.twitter.com/2014/how-to-objective-c-initializer-patterns中已经很详细的解释了。

      【讨论】:

        猜你喜欢
        • 2023-04-09
        • 1970-01-01
        • 2014-07-27
        • 2020-10-22
        • 1970-01-01
        • 2016-05-27
        • 2020-08-10
        • 2015-02-24
        • 2015-11-24
        相关资源
        最近更新 更多