我们在XCode 6中引入了这个新宏:NS_DESIGNATED_INITIALIZER
我在网上搜索,但无法找到任何关于如何使用它的好文档。
从语法上讲,我们可以像:
一样使用它- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
但是使用这个宏标记初始化程序有什么可能的优点,以及使用它时我们应该注意什么?
我主要对这个宏的用例感兴趣。任何链接/文档将不胜感激。
答案 0 :(得分:51)
在http://useyourloaf.com/blog/2014/08/19/xcode-6-objective-c-modernization.html中很好地解释了NS_DESIGNATED_INITIALIZER
的使用:
指定的初始化程序保证对象完全初始化 通过向超类发送初始化消息。该 实现细节对于类的用户来说变得很重要 他们将它分类。指定初始化程序的规则详细说明:
- 指定的初始化程序必须调用(通过super)指定的 超类的初始化程序。其中NSObject是超类 只是[超级初始化]。
- 任何便利初始化程序必须调用另一个 类中的初始化程序 - 最终导致指定的 初始化。
- 具有指定初始值设定项的类必须实现所有 超类的指定初始化者。
例如,如果你的界面是
@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: "")
}
}
答案 1 :(得分:12)
我最常用的方法是:
@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
(在这种情况下为NSObject
的{{1}}) - 我们使用init:
将此方法标记为不需要。或者您可以覆盖它以使用默认参数调用指定的初始化程序。
答案 2 :(得分:1)
指定的初始化器定义了在子类化时我们如何构造初始化器;它们是您班级的“规范初始化者”。无论您调用的超类链中指定的初始化程序如何,它都保证可靠,并且始终从最远的祖先到最远的后代。
指定的初始值设定项未定义创建对象时应使用的初始值设定项。它在https://blog.twitter.com/2014/how-to-objective-c-initializer-patterns中得到了很好的解释。