循环Initialization in subclass: Swift and SpriteKit
Circling the drain of Initialization in subclass: Swift and SpriteKit
我想创建一个比 SpriteNode 的 touchesBegan 级别更高的 SKShapeNode,所以当我想从这个精灵的 touchesBegun 事件将 SKShapeNode 添加到屏幕时,形状已经存在,我只需添加它从 touchesBegan 覆盖内到屏幕。
TL;DR,在我的 SKSpriteNode 中,我正在尝试预构建 SKShapeNode,它将在 Sprite 被触摸时用作动画环。
我想用 variable/constants 创建 SKShapeNode,这样我就可以轻松地编辑它的值...
所以在子的根部class 我有颜色、大小和线宽的变量,准备用于创建 SKShapeNode...
class Balls: SKSpriteNode {
var ringSize: CGFloat = 64
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 16
....
再往下,但仍然在 class 的根部,我创建了我的戒指:
let ring = SKShapeNode(circleOfRadius: ringSize)
我立刻受到了亲切而神秘的欢迎:
Can not use instance member 'ringSize' within property initializer,
property initializers run before 'self' is available.
很好。好的。我得到它。您想认为应该在将值分配给 self 之前完成对 class 的功能调用以创建 属性 。无论是这里还是那里,我认为我变得狡猾并且可以通过将所有内容包装在一个函数中来解决这个问题:
class Balls: SKSpriteNode {
var ringSize: CGFloat = 64
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 16
var myRing = SKShapeNode()
func createRing() -> SKShapeNode{
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}
这不会产生任何错误,我兴奋起来。
所以我添加了另一行来创建实际的环:
....
myRing = createRing()
又死了:
! Expected
declaration
我完全不知道这意味着什么,开始随机尝试奇怪的事情。
其中一个正在进入我已经很乱的便利初始化程序并在其中添加 myRing = createRing()
... 这有效!
这是如何以及为什么工作的,这是绕过初始化耗尽的 best/right/proper 方法吗?
:: 编辑::更新::完整代码上下文::
这是完整的 class,其中包含我奇怪且被误解的初始化程序。
import SpriteKit
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 8
var myRing = SKShapeNode()
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
}
convenience init() {
self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))
myRing = createRing()
addChild(myRing)
print("I'm on the screen")
explodeGroup = create_explosionActionGroup()
}
convenience init(color: UIColor, size: CGSize, position: CGPoint) {
self.init(color: color, size: size)
self.position = position
myRing = createRing()
explodeGroup = create_explosionActionGroup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createRing() -> SKShapeNode{
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}
我对你把
放在哪里有点困惑
myRing = createRing()
行代码,但我想知道此设置是否有助于解决您的问题
lazy var myRing: SKShapeNode = {
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}()
这样 myRing
将在访问时创建,应该在 Balls
class 实例化之后创建,这意味着 ringSize、ringColor 和 ringWidth 都将存在。
根据你的更新,我认为你最好的选择可能是将你的三个环变量设为“static let”。这样它们就会存在并在初始化 main class 之前具有设定值。您看到的错误是因为您创建了实例变量。这些仅在实例已初始化时存在。因此,如果您尝试调用 ring 方法作为变量的声明,或者如果您在 init before self/super init 被调用之前这样做,那么实例变量将不会无障碍。您添加的最新代码应该可以正常工作,因为您在尝试生成环之前创建了实例。我希望这是有道理的并有所帮助。
您的 createRing() 方法在 Ball 内部 class 因此您需要先创建一个 Ball 实例。
简单方法 - 您可以将实例的创建更改为
let ball = Balls()
let myRing = ball.createRing()
And am instantly greeted with the lovingly cryptic:
Can not use instance member 'ringSize' within property initializer, property initializers run before 'self' is available.
因此解决此问题的一种方法是使默认 ringSize
可用另一种方式使用,例如
static let defaultRingSize: CGFloat = 64
var ringSize: CGFloat = Circle.defaultRingSize
let ring = SKShapeNode(circleOfRadius: Circle.defaultRingSize)
...但我怀疑你为什么会有这样的 var ringSize
属性。你不应该在它上面有一个 didSet
观察者,这样如果你改变它的值,你可以更新 ring
的形状吗?
Dead again:
! Expected declaration
在你的问题中,你并不清楚你是如何触发这个的,但我猜你试过这样的事情:
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96
var myRing = SKShapeNode()
myRing = createRing() // “Expected declaration” error on this line
这里的问题是您在 class 的正文中放置了一个语句,但正文中只允许声明。
One of them is heading into my already messy convenience initializer and adding myRing = createRing() in there... and this WORKS!
How and why does this work
您的所有 class 自己的实例变量都必须在 super.init
调用之前初始化。由于 myRing
有一个默认值,编译器在调用 super.init
之前有效地将 myRing
的初始化插入到您指定的初始化程序中,如下所示:
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
// Compiler-inserted initialization of myRing to the default
// value you specified:
myRing = SKShapeNode()
super.init(texture: texture, color: color, size: size)
}
由于您声明了 var myRing
,您可以稍后将其更改为您真正想要的自定义 SKShapeNode
。
is this the best/right/proper way to be circling the drain of initialization?
好吧,“circling the drain”的意思是“失败”,所以我猜你是在问这是否是初始化失败的“best/right/proper 方式”……我想这不是失败的最佳方式,因为你最终并没有真正失败。
或者你的意思可能是“我讨厌 Swift 进行初始化的方式,所以我要抛出一些阴影”,在这种情况下,you ain't seen nothin' yet.
但也许您的意思是“这是初始化我的实例的 best/right/proper 方式吗”,在这种情况下,嗯,“最好”、“正确”和“适当”是相当主观的。
但我可以客观地指出,您正在创建一个 SKShapeNode
(作为 myRing
的默认值)只是为了立即将其丢弃并创建另一个 SKShapeNode
。所以这是一种浪费。您还在两个便利初始值设定项中调用了 createRing
,但您可以将它们分解到指定的初始值设定项中。
但我什至不会那样做。 SKShapeNode
的 path
属性 是可设置的,因此您可以创建默认 SKShapeNode
,然后在调用 super.init
后更改其 path
.这也使得处理对 ringSize
和其他属性的更改变得更加容易,因为您可以通过一个知道如何使 myRing
匹配属性的方法汇集所有更改。
我可能会这样写你的 class:
import SpriteKit
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96 {
// Use an observer to update myRing if this changes.
didSet { configureMyRing() }
}
var ringColor = SKColor.white {
didSet { configureMyRing() }
}
var ringWidth: CGFloat = 8 {
didSet { configureMyRing() }
}
// This can be a let instead of a var because I'm never going to
// set it to a different object. Note that I'm not bothering to
// initialize myRing's path or any other property here, because
// I can just call configureMyRing in my init (after the call to
// super.init).
let myRing = SKShapeNode()
override init(texture: SKTexture?, color: SKColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
// Call this now to set up myRing's path and other properties.
configureMyRing()
}
convenience init() {
self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))
// No need to do anything to myRing now, because my designated
// initializer set it up completely.
addChild(myRing)
print("I'm on the screen")
// Commented out because you didn't provide this property
// or method in your question.
// explodeGroup = create_explosionActionGroup()
}
convenience init(color: SKColor, size: CGSize, position: CGPoint) {
self.init(color: color, size: size)
self.position = position
// Commented out because you didn't provide this property
// or method in your question.
// explodeGroup = create_explosionActionGroup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureMyRing() {
myRing.path = CGPath(ellipseIn: CGRect(x: -ringSize / 2, y: -ringSize / 2, width: ringSize, height: ringSize), transform: nil)
myRing.strokeColor = ringColor
myRing.lineWidth = ringWidth
}
}
我想创建一个比 SpriteNode 的 touchesBegan 级别更高的 SKShapeNode,所以当我想从这个精灵的 touchesBegun 事件将 SKShapeNode 添加到屏幕时,形状已经存在,我只需添加它从 touchesBegan 覆盖内到屏幕。
TL;DR,在我的 SKSpriteNode 中,我正在尝试预构建 SKShapeNode,它将在 Sprite 被触摸时用作动画环。
我想用 variable/constants 创建 SKShapeNode,这样我就可以轻松地编辑它的值...
所以在子的根部class 我有颜色、大小和线宽的变量,准备用于创建 SKShapeNode...
class Balls: SKSpriteNode {
var ringSize: CGFloat = 64
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 16
....
再往下,但仍然在 class 的根部,我创建了我的戒指:
let ring = SKShapeNode(circleOfRadius: ringSize)
我立刻受到了亲切而神秘的欢迎:
Can not use instance member 'ringSize' within property initializer, property initializers run before 'self' is available.
很好。好的。我得到它。您想认为应该在将值分配给 self 之前完成对 class 的功能调用以创建 属性 。无论是这里还是那里,我认为我变得狡猾并且可以通过将所有内容包装在一个函数中来解决这个问题:
class Balls: SKSpriteNode {
var ringSize: CGFloat = 64
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 16
var myRing = SKShapeNode()
func createRing() -> SKShapeNode{
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}
这不会产生任何错误,我兴奋起来。
所以我添加了另一行来创建实际的环: ....
myRing = createRing()
又死了:
! Expected declaration
我完全不知道这意味着什么,开始随机尝试奇怪的事情。
其中一个正在进入我已经很乱的便利初始化程序并在其中添加 myRing = createRing()
... 这有效!
这是如何以及为什么工作的,这是绕过初始化耗尽的 best/right/proper 方法吗?
:: 编辑::更新::完整代码上下文::
这是完整的 class,其中包含我奇怪且被误解的初始化程序。
import SpriteKit
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96
var ringColor: SKColor = SKColor.white
var ringWidth: CGFloat = 8
var myRing = SKShapeNode()
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
}
convenience init() {
self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))
myRing = createRing()
addChild(myRing)
print("I'm on the screen")
explodeGroup = create_explosionActionGroup()
}
convenience init(color: UIColor, size: CGSize, position: CGPoint) {
self.init(color: color, size: size)
self.position = position
myRing = createRing()
explodeGroup = create_explosionActionGroup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createRing() -> SKShapeNode{
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}
我对你把
放在哪里有点困惑 myRing = createRing()
行代码,但我想知道此设置是否有助于解决您的问题
lazy var myRing: SKShapeNode = {
let ring = SKShapeNode(circleOfRadius: ringSize)
ring.strokeColor = ringColor
ring.lineWidth = ringWidth
return ring
}()
这样 myRing
将在访问时创建,应该在 Balls
class 实例化之后创建,这意味着 ringSize、ringColor 和 ringWidth 都将存在。
根据你的更新,我认为你最好的选择可能是将你的三个环变量设为“static let”。这样它们就会存在并在初始化 main class 之前具有设定值。您看到的错误是因为您创建了实例变量。这些仅在实例已初始化时存在。因此,如果您尝试调用 ring 方法作为变量的声明,或者如果您在 init before self/super init 被调用之前这样做,那么实例变量将不会无障碍。您添加的最新代码应该可以正常工作,因为您在尝试生成环之前创建了实例。我希望这是有道理的并有所帮助。
您的 createRing() 方法在 Ball 内部 class 因此您需要先创建一个 Ball 实例。
简单方法 - 您可以将实例的创建更改为
let ball = Balls()
let myRing = ball.createRing()
And am instantly greeted with the lovingly cryptic:
Can not use instance member 'ringSize' within property initializer, property initializers run before 'self' is available.
因此解决此问题的一种方法是使默认 ringSize
可用另一种方式使用,例如
static let defaultRingSize: CGFloat = 64
var ringSize: CGFloat = Circle.defaultRingSize
let ring = SKShapeNode(circleOfRadius: Circle.defaultRingSize)
...但我怀疑你为什么会有这样的 var ringSize
属性。你不应该在它上面有一个 didSet
观察者,这样如果你改变它的值,你可以更新 ring
的形状吗?
Dead again:
! Expected declaration
在你的问题中,你并不清楚你是如何触发这个的,但我猜你试过这样的事情:
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96
var myRing = SKShapeNode()
myRing = createRing() // “Expected declaration” error on this line
这里的问题是您在 class 的正文中放置了一个语句,但正文中只允许声明。
One of them is heading into my already messy convenience initializer and adding myRing = createRing() in there... and this WORKS!
How and why does this work
您的所有 class 自己的实例变量都必须在 super.init
调用之前初始化。由于 myRing
有一个默认值,编译器在调用 super.init
之前有效地将 myRing
的初始化插入到您指定的初始化程序中,如下所示:
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
// Compiler-inserted initialization of myRing to the default
// value you specified:
myRing = SKShapeNode()
super.init(texture: texture, color: color, size: size)
}
由于您声明了 var myRing
,您可以稍后将其更改为您真正想要的自定义 SKShapeNode
。
is this the best/right/proper way to be circling the drain of initialization?
好吧,“circling the drain”的意思是“失败”,所以我猜你是在问这是否是初始化失败的“best/right/proper 方式”……我想这不是失败的最佳方式,因为你最终并没有真正失败。
或者你的意思可能是“我讨厌 Swift 进行初始化的方式,所以我要抛出一些阴影”,在这种情况下,you ain't seen nothin' yet.
但也许您的意思是“这是初始化我的实例的 best/right/proper 方式吗”,在这种情况下,嗯,“最好”、“正确”和“适当”是相当主观的。
但我可以客观地指出,您正在创建一个 SKShapeNode
(作为 myRing
的默认值)只是为了立即将其丢弃并创建另一个 SKShapeNode
。所以这是一种浪费。您还在两个便利初始值设定项中调用了 createRing
,但您可以将它们分解到指定的初始值设定项中。
但我什至不会那样做。 SKShapeNode
的 path
属性 是可设置的,因此您可以创建默认 SKShapeNode
,然后在调用 super.init
后更改其 path
.这也使得处理对 ringSize
和其他属性的更改变得更加容易,因为您可以通过一个知道如何使 myRing
匹配属性的方法汇集所有更改。
我可能会这样写你的 class:
import SpriteKit
class Circle: SKSpriteNode {
var ringSize: CGFloat = 96 {
// Use an observer to update myRing if this changes.
didSet { configureMyRing() }
}
var ringColor = SKColor.white {
didSet { configureMyRing() }
}
var ringWidth: CGFloat = 8 {
didSet { configureMyRing() }
}
// This can be a let instead of a var because I'm never going to
// set it to a different object. Note that I'm not bothering to
// initialize myRing's path or any other property here, because
// I can just call configureMyRing in my init (after the call to
// super.init).
let myRing = SKShapeNode()
override init(texture: SKTexture?, color: SKColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
// Call this now to set up myRing's path and other properties.
configureMyRing()
}
convenience init() {
self.init(color: SKColor.clear, size: CGSize(width: 100, height: 100))
// No need to do anything to myRing now, because my designated
// initializer set it up completely.
addChild(myRing)
print("I'm on the screen")
// Commented out because you didn't provide this property
// or method in your question.
// explodeGroup = create_explosionActionGroup()
}
convenience init(color: SKColor, size: CGSize, position: CGPoint) {
self.init(color: color, size: size)
self.position = position
// Commented out because you didn't provide this property
// or method in your question.
// explodeGroup = create_explosionActionGroup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureMyRing() {
myRing.path = CGPath(ellipseIn: CGRect(x: -ringSize / 2, y: -ringSize / 2, width: ringSize, height: ringSize), transform: nil)
myRing.strokeColor = ringColor
myRing.lineWidth = ringWidth
}
}