如何初始化相互依赖的属性

我想把照片移到底部。如果我按下一个按钮,图片应该向下移动1。

我添加了图片和一个按钮:

var corX = 0
var corY = 0


var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton


var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));  //


override func viewDidLoad() {
super.viewDidLoad()


panzer.image = image;    //
self.view.addSubview(panzer);    //


runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}

至少我在函数中说过“ fahren”将图片向下移动1。

func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}

所以我的问题是: 我得到了几个错误,这些 CorX 和 CorY 的东西。没有它们,它的工作完美,但比它像一个一次性使用按钮。错误是: ViewController。类型没有名为 corX 和 ViewController 的成员。类型没有成员名称 panzer,在这里我得到了我在//中显示哪些行的错误。

附言: 我使用 Xcode Beta 5

下面是完整的代码,没有其他内容:

import UIKit


class ViewController: UIViewController {


var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));


override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);


runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}


func fahren(){
corY += 100
panzer.frame = CGRectMake(corX, corY, 30, 40)
self.view.addSubview(panzer);
}
}
13394 次浏览

@ MartinR 指出了这里的主要问题:

var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))

问题是 Swift 默认初始化器不能引用另一个属性的值,因为在初始化时,该属性还不存在(因为实例本身还不存在)。基本上,在 panzer的默认初始化器中,您隐式地引用了 self.corXself.corY-但是没有 self,因为 self正是我们正在创建的。

一种变通方法是使初始化程序变得懒惰:

class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}

这是合法的,因为 panzer直到后来才被初始化,那时它才首次被您的实际代码引用。到那时,self及其属性已经存在。

你的受供养财产须符合以下条件:

  1. lazy
  2. 有一个明确的 : Type
  3. 使用 self.访问其他属性

例如:

let original = "foo"


// Good:
lazy var depend: String = self.original


// Error:
var noLazy: String = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noType         = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noSelf: String =      original // Error: Instance member 'original' cannot be used on type 'YourClass'

我正在回答这个问题的题目:

惰性属性和计算属性都可以帮助您处理在初始化对象之前属性的初始值为 没有的情况。但也有一些不同之处。我用大胆的方式强调了这些差异。

如果你只是需要在初始化其他变量之后初始化一个变量,那么你应该使用 lazy,即如果你只是想简单地添加一个延迟(所以所有需要的属性都在初始化之前) ,那么使用延迟是正确的方法。

但是,如果你需要 经常基于另一个变量改变一个变量,那么你需要一个计算属性,将工作 双向的:

  • 如果计算属性 准备好了,那么它将设置变量的相关存储属性
  • 如果存储属性被设置(或者被重置 再来一次) ,那么它将触发当时计算属性的更改。

如果您更改惰性属性的值,它将不会影响它所基于的故事属性


使用延迟属性的一个很好的例子是,一旦你有了 firstNamelastName,那么你将延迟实例化一个 fullName,并且很可能你永远不会改变你对象的 firstName lastName,你的 fullName 只是一次性的..。 或者只能由 lazy属性完成的事情是,直到您不访问属性 它永远不会被初始化为止,因此这将导致类的初始化负载 减少。加载 沉重计算。

另外,使用 lazy信号给其他开发者: “嘿,首先去阅读其他属性,了解他们是什么... 然后来到这个懒惰的属性... 因为这个值是基于他们 + 这可能是一个沉重的计算,不应该访问得太早...”

至于计算属性一个很好的例子是,如果你设置的温度为 华氏度,那么你也希望你的 摄氏度温度改变它的值... 如果你设置的 摄氏度温度,那么你想再次改变你的 华氏度值。

因此,计算属性会增加额外的计算量... ... 如果你的计算非常简单,并且不经常被调用,那么没有什么可担心的,但是如果它被调用得太频繁或者非常占用 CPU,那么最好考虑其他选项... ..。

//
//  ViewController.swift
//
//  Created by Shivank Agarwal on 19/05/18.
//  Copyright © 2018 Shivank Agarwal. All rights reserved.
//


import UIKit


class ViewController: UIViewController {


var corX = 0
var corY = 0
var runter: UIButton = UIButton()
var image = UIImage(named: "panzerBlau.jpg")
var panzer = UIImageView()


override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
runter.backgroundColor = UIColor.red
view.addSubview(runter)
view.addSubview(panzer)
runter.addTarget(self, action: Selector(("fahren")), for:UIControlEvents.touchUpInside)
}


private func fahren(){
corY += 100
}


private func updatePanzerFrame(){
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
}
}


Note: Do not add panzer imageView every time when user tap only add it on viewDidLoad()