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

How to initialize properties that depend on each other

提问人:Lukas Köhl 提问时间:9/16/2014 最后编辑:AmitaiBLukas Köhl 更新时间:4/23/2020 访问量:14023

问:

我想让图片移动到底部。如果我按下一个按钮,图片应该向下移动 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);
    }
}
Xcode Swift 框架 cgrectmake

评论

2赞 matt 9/16/2014
“我使用 Xcode Beta5”为什么?Xcode 6 已经成为 GM。 始终使用最新版本,尤其是 Swift 语言在不断变化。
0赞 Lukas Köhl 9/16/2014
是的,我知道,但我是从度假来的,还没有这样做。但是更新并不能解决问题。
0赞 matt 9/16/2014
听起来你把代码放错了地方,例如 不在声明的顶层。var corX = 0class
2赞 rob mayoff 9/16/2014
发布整个源文件。
1赞 matt 9/16/2014
你能展示一下周围的背景吗?(是的,就像@robmayoff说的那样。这次请更加努力地正确格式化它。没有人喜欢懒得格式化代码的海报......

答:

67赞 matt 9/16/2014 #1

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

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

问题在于 Swift 默认初始值设定项不能引用另一个属性的值,因为在初始化时,该属性尚不存在(因为实例本身尚不存在)。基本上,在默认的初始值设定项中,您隐含地引用了 和 - 但没有,因为这正是我们正在创建的。panzerself.corXself.corYselfself

一种解决方法是使初始值设定项延迟:

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

这是合法的,因为直到以后,当它第一次被你的实际代码引用时,它才会被初始化。到那时,它的属性已经存在。panzerself

评论

6赞 Martin R 9/16/2014
你试过编译吗?首先,它在当前的 Xcode 版本中没有,即便如此,我仍然收到相同的错误消息。lazy@
1赞 matt 9/16/2014
@MartinR 好电话。现在都修好了!
0赞 Mike S 9/16/2014
@matt我仍然收到该更改的错误:'ViewController -> () -> ViewController!' does not have a member named 'corX'
2赞 matt 9/16/2014
@MikeS 听起来像是你输入的,而不是复制和粘贴的。你不能遗漏任何东西。例如,如果省略 UIImageView 类型声明,则会收到该错误。或者,如果您省略了延迟声明,则会出现该错误。这一切都是必要的。直接从浏览器中复制并粘贴到代码中。它在当前版本的 Xcode (Xcode 6 GM) 中编译得很好。
3赞 kelin 6/30/2017
@matt,你的指示不起作用,接受它。只有 pkamb 告诉删除编译器错误应满足的 3 个条件:和 。虽然你用过,但你没有说这很重要。你的答案要长得多,更难读懂。我认为它有更多的赞成票是不公平的。lazy.self: Type: Type
10赞 pkamb 2/9/2017 #2

您的依赖属性需要:

  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'
1赞 mfaani 5/4/2017 #3

我正在解决问题的标题:

惰性属性和计算属性都可以帮助您处理属性的初始值在初始化对象之前未知的情况。但也有一些不同之处。我用粗体突出显示了差异。

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

但是,如果你需要不断地根据另一个变量来改变一个变量,那么你需要一个双向工作的计算属性:

  • 如果 computed 属性设置了,则它将设置变量,其相关的存储属性
  • 如果存储的属性被设置(或再次重置),那么它将触发然后计算属性的更改。

如果更改 lazy 属性的值,则不会影响它所基于的 storied 属性。看这里


使用惰性属性的一个很好的例子是,一旦你有了 &,然后你就会懒惰地实例化一个,并且很可能你永远不会改变你的对象的 firstName lastName,你的 fullName 是一次性的...... 或者,也许只有属性才能完成的事情是,直到您不访问该属性,它永远不会被初始化,因此这将减少类的初始化负载。加载繁重的计算。firstNamelastNamefullNamelazy

此外,使用遗嘱向其他开发人员发出信号:“嘿,首先去阅读其他属性并了解它们是什么......那就来这个懒惰的物业吧......由于其值基于它们+,因此这可能是一个繁重的计算,不应过早访问......”lazy

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

因此,计算属性将增加额外的计算...如果你的计算非常简单,并且调用的频率不太高,那么就没什么好担心的,但是如果它调用得太频繁或非常消耗CPU,那么最好考虑其他选项......

-1赞 CrazyPro007 5/20/2018 #4
//
//  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()

评论

1赞 pkamb 4/23/2020
这个答案如何适用于这个问题?