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

我想要一张照片移到底部。 如果我按下button,图片应该向下移动1。

我添加了图片和一个button:

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) } 

至less我用“fahren”来表示把图片向下移动1。

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

所以我的问题是:我得到这些CorX和CorY的东西几个错误。 没有它们,它可以完美地工作,而不是像一次性使用的button。 错误是: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默认的初始化器不能引用另一个属性的值,因为在初始化的时候,属性还不存在(因为实例本身还不存在)。 基本上,在self.corX的默认初始化程序中,你隐含地指的是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' 

我正在处理这个问题的标题:

延迟属性和计算属性都可以帮助您处理属性的初始值在对象初始化之后知道的情况。 但是有一些差异。 我用粗体突出了这些差异。

如果在初始化一些其他variables之后,你只需要初始化一个variables,那么你应该使用lazy即如果点是简单地添加一个延迟(所以所有需要的属性之前被初始化),那么使用懒惰是正确的路要走为了它。

但是如果你需要不断地改变一个基于另一个variables的variables,那么你需要一个可以同时工作的计算属性:

  • 如果计算出的属性设置,那么它设置variables的相关存储属性
  • 如果存储的属性被设置(或者被重新设置),那么它将触发在计算的属性中的改变。

如果您更改了懒惰属性的值,则不会影响其基于的传记属性。 看到这里


使用懒惰属性的一个很好的例子是,一旦你有firstNamelastName那么你会懒惰地实例化一个完整的名称,可能你永远不会改变你的对象的名字姓氏,你的全名是一次只…或者可能只有通过lazy属性才能完成,直到你不访问属性,它将永远不会被初始化 ,因此这将减less你的类的初始化负载。 加载繁重的计算。

另外使用lazy 信号给其他开发者:“嘿,先去看看其他的属性,了解它们是什么…然后来到这个懒惰的属性…因为这是基于它的价值+这可能是一个大量的计算,不应该太早访问…“

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

因此,计算属性会增加额外的计算…如果你的计算非常简单,不会太频繁调用,那么没有什么可担心的,但是如果它被调用得太频繁或者非常耗费CPU,那么它可能会更好想想别的select