Swift中初始化方法的分段
Segmentation of Initialization Method in Swift
作为一名 OS X/iOS 开发人员多年以来,我对 Apples Swift 语言产生了浓厚的兴趣。为了保持我的初始化方法简洁明了,我将初始化的各个部分隔离到各个方法中。特别是在处理长初始化时——就像经常在 SpriteKit SKScene
subclass 中发现的那样,这有助于我保持一个干净简单的结构。这是它的样子:
典型 Objective-C Class:
@interface SomeScene ()
@property (strong, nonatomic) SKSpriteNode* backgroundNode;
@property (strong, nonatomic) SKNode* hudNode;
@property (strong, nonatomic) SKSpriteNode* playerNode;
@end
@implementation SomeScene
-(id)init {
if (self = [super init]) {
[self setupBackgroundNode];
[self setupHUD];
[self setupPlayer];
}
return self;
}
-(void)setupBackgroundNode {
self.backgroundNode = [SKSpriteNode new];
self.backgroundNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.backgroundNode];
}
-(void)setupHUD {
self.hudNode = [SKSpriteNode new];
self.hudNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.hudNode];
}
-(void)setupPlayer {
self.playerNode = [SKSpriteNode new];
self.playerNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.playerNode];
}
@end
这只是一个演示,向您展示我如何将初始化的不同部分封装到各个方法中,然后在调用 -(id)init
时调用这些方法。我在我的任何项目中都使用这种模式。
现在通过学习 Swift 我了解了基本概念和可选值,并尝试适应略有不同的初始化类型。为了保持上面显示的我自己的 Objective-C 模式,Swift 代码将如下所示:
Swift中的相同class:
class SomeScene: SKScene {
var backgroundNode: SKSpriteNode?
var playerNode: SKSpriteNode?
var hudNode: SKSpriteNode?
override convenience init() {
self.init()
self.setupBackground()
self.setupPlayer()
self.setupHUD()
}
func setupBackground() {
backgroundNode = SKSpriteNode(/*Some Initialisation Parameters*/)
backgroundNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(backgroundNode!);
}
func setupPlayer() {
playerNode = SKSpriteNode(/*Some Initialisation Parameters*/)
playerNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(playerNode!);
}
func setupHUD() {
hudNode = SKSpriteNode(/*Some Initialisation Parameters*/)
hudNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(hudNode!);
}
}
这确实像预期的那样工作,但我问自己这是否是使用可选值的正确方法。恐怕我滥用它们只是为了将 Objective-C 样式转换为 Swift。我真的必须在每个 属性 之后都写上所有问号吗?这是什么东西还是更像是一种切换编译器错误的方法 属性 "might be nil" - 这就是 Apples 文档对 ?-suffix 的描述。
我提出这个问题是因为以这种方式使用 swift 感觉有点奇怪。有没有更好的方法来封装 init 方法的代码部分/块,以防止它不断增长和失去焦点?
Do I really have to write all that question marks after every property?
基本上是的。 Swift 对初始化程序有严格的规定。您必须在初始化结束时初始化所有实例属性。如果要将实例属性的初始化移出初始化程序,则必须以其他方式为这些实例属性提供默认初始值。您可以使用等号和显式值来做到这一点,或者您可以使用 Optional 来做到这一点,因为它会自动分配 nil
.
这就是为什么出口 (@IBOutlet
) 通常是可选的。我们知道它们不会在初始化期间被初始化,所以我们需要一个临时值,直到它们 在 nib 加载时被初始化。
另外,通常的做法是对任何实例使用 Optionals 属性,只有在实例本身完全初始化之后才能对其进行初始化。例如,您可能需要执行一些耗时的操作,而您不想在初始化程序中执行这些操作。
但是,还有另一种方法可能适用于您的某些实例属性:将它们标记为 lazy
并提供默认初始化程序,即定义和调用函数。例如:
lazy var prog : UIProgressView = {
let p = UIProgressView(progressViewStyle: .Default)
p.alpha = 0.7
p.trackTintColor = UIColor.clearColor()
p.progressTintColor = UIColor.blackColor()
p.frame = CGRectMake(0, 0, self.view.bounds.size.width, 20)
p.progress = 1.0
return p
}()
那有封装的好处。我们提供了一个默认值,即从定义和调用函数返回的东西,所以我们不必在初始化程序中初始化。但是编译器允许我们推迟 运行 定义和调用函数,直到其他代码第一次实际访问该实例 属性。这种模式对你来说可能更有意义。
我还发现自己经常使用计算机属性。在许多情况下,这是另一种很好的替代模式。
所以基本上是的,你所做的是正确的,但是 Swift 有一些其他的模式通常可以使它变得不必要,当你来自 Objective-C 时你会想要也对这些模式感到满意。
作为一名 OS X/iOS 开发人员多年以来,我对 Apples Swift 语言产生了浓厚的兴趣。为了保持我的初始化方法简洁明了,我将初始化的各个部分隔离到各个方法中。特别是在处理长初始化时——就像经常在 SpriteKit SKScene
subclass 中发现的那样,这有助于我保持一个干净简单的结构。这是它的样子:
典型 Objective-C Class:
@interface SomeScene ()
@property (strong, nonatomic) SKSpriteNode* backgroundNode;
@property (strong, nonatomic) SKNode* hudNode;
@property (strong, nonatomic) SKSpriteNode* playerNode;
@end
@implementation SomeScene
-(id)init {
if (self = [super init]) {
[self setupBackgroundNode];
[self setupHUD];
[self setupPlayer];
}
return self;
}
-(void)setupBackgroundNode {
self.backgroundNode = [SKSpriteNode new];
self.backgroundNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.backgroundNode];
}
-(void)setupHUD {
self.hudNode = [SKSpriteNode new];
self.hudNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.hudNode];
}
-(void)setupPlayer {
self.playerNode = [SKSpriteNode new];
self.playerNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.playerNode];
}
@end
这只是一个演示,向您展示我如何将初始化的不同部分封装到各个方法中,然后在调用 -(id)init
时调用这些方法。我在我的任何项目中都使用这种模式。
现在通过学习 Swift 我了解了基本概念和可选值,并尝试适应略有不同的初始化类型。为了保持上面显示的我自己的 Objective-C 模式,Swift 代码将如下所示:
Swift中的相同class:
class SomeScene: SKScene {
var backgroundNode: SKSpriteNode?
var playerNode: SKSpriteNode?
var hudNode: SKSpriteNode?
override convenience init() {
self.init()
self.setupBackground()
self.setupPlayer()
self.setupHUD()
}
func setupBackground() {
backgroundNode = SKSpriteNode(/*Some Initialisation Parameters*/)
backgroundNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(backgroundNode!);
}
func setupPlayer() {
playerNode = SKSpriteNode(/*Some Initialisation Parameters*/)
playerNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(playerNode!);
}
func setupHUD() {
hudNode = SKSpriteNode(/*Some Initialisation Parameters*/)
hudNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(hudNode!);
}
}
这确实像预期的那样工作,但我问自己这是否是使用可选值的正确方法。恐怕我滥用它们只是为了将 Objective-C 样式转换为 Swift。我真的必须在每个 属性 之后都写上所有问号吗?这是什么东西还是更像是一种切换编译器错误的方法 属性 "might be nil" - 这就是 Apples 文档对 ?-suffix 的描述。
我提出这个问题是因为以这种方式使用 swift 感觉有点奇怪。有没有更好的方法来封装 init 方法的代码部分/块,以防止它不断增长和失去焦点?
Do I really have to write all that question marks after every property?
基本上是的。 Swift 对初始化程序有严格的规定。您必须在初始化结束时初始化所有实例属性。如果要将实例属性的初始化移出初始化程序,则必须以其他方式为这些实例属性提供默认初始值。您可以使用等号和显式值来做到这一点,或者您可以使用 Optional 来做到这一点,因为它会自动分配 nil
.
这就是为什么出口 (@IBOutlet
) 通常是可选的。我们知道它们不会在初始化期间被初始化,所以我们需要一个临时值,直到它们 在 nib 加载时被初始化。
另外,通常的做法是对任何实例使用 Optionals 属性,只有在实例本身完全初始化之后才能对其进行初始化。例如,您可能需要执行一些耗时的操作,而您不想在初始化程序中执行这些操作。
但是,还有另一种方法可能适用于您的某些实例属性:将它们标记为 lazy
并提供默认初始化程序,即定义和调用函数。例如:
lazy var prog : UIProgressView = {
let p = UIProgressView(progressViewStyle: .Default)
p.alpha = 0.7
p.trackTintColor = UIColor.clearColor()
p.progressTintColor = UIColor.blackColor()
p.frame = CGRectMake(0, 0, self.view.bounds.size.width, 20)
p.progress = 1.0
return p
}()
那有封装的好处。我们提供了一个默认值,即从定义和调用函数返回的东西,所以我们不必在初始化程序中初始化。但是编译器允许我们推迟 运行 定义和调用函数,直到其他代码第一次实际访问该实例 属性。这种模式对你来说可能更有意义。
我还发现自己经常使用计算机属性。在许多情况下,这是另一种很好的替代模式。
所以基本上是的,你所做的是正确的,但是 Swift 有一些其他的模式通常可以使它变得不必要,当你来自 Objective-C 时你会想要也对这些模式感到满意。