提问人:Lukas Köhl 提问时间:9/16/2014 最后编辑:AmitaiBLukas Köhl 更新时间:4/23/2020 访问量:14023
如何初始化相互依赖的属性
How to initialize properties that depend on each other
问:
我想让图片移动到底部。如果我按下一个按钮,图片应该向下移动 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)
}
至少我在函数“华氏度”中说将图片向下移动 1。
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
所以我的问题是:我在这些 corX 和 corY 上遇到了几个错误。没有它们,它可以完美地工作,但就像一个一次性按钮。错误是:ViewController.Type 没有名为 corX 的成员,ViewController.Type 没有成员名称 panzer 我得到的错误 我犯了错误 // 在哪些行中显示。
PS:我使用Xcode Beta5
这是完整的代码,没有其他任何内容:
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);
}
}
答:
@MartinR指出了这里的主要问题:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
问题在于 Swift 默认初始值设定项不能引用另一个属性的值,因为在初始化时,该属性尚不存在(因为实例本身尚不存在)。基本上,在默认的初始值设定项中,您隐含地引用了 和 - 但没有,因为这正是我们正在创建的。panzer
self.corX
self.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
评论
lazy
@
'ViewController -> () -> ViewController!' does not have a member named 'corX'
lazy
.self
: Type
: Type
您的依赖属性需要:
lazy
- 有一个明确的
: Type
- 用于访问其他属性
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
但是,如果你需要不断地根据另一个变量来改变一个变量,那么你需要一个双向工作的计算属性:
- 如果 computed 属性设置了,则它将设置变量,其相关的存储属性
- 如果存储的属性被设置(或再次重置),那么它将触发然后计算属性的更改。
如果更改 lazy 属性的值,则不会影响它所基于的 storied 属性。看这里
使用惰性属性的一个很好的例子是,一旦你有了 &,然后你就会懒惰地实例化一个,并且很可能你永远不会改变你的对象的 firstName lastName,你的 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()
评论
var corX = 0
class