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


77

我希望图片移到底部。如果按按钮,图片应向下移动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.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);
    }
}

2
“我使用Xcode Beta5”为什么?Xcode 6已经成为GM。始终使用最新版本,尤其是。随着Swift语言的不断变化。
马特2014年

是的,我知道,但是我是从一个假期来的,还没有做到。但是更新无法解决问题。
LukasKöhl2014年

听起来您将代码放置在错误的位置,例如var corX = 0不在class声明的顶层。
马特2014年

2
发布整个源文件。
rob mayoff

1
您能显示周围的环境吗?(是的,就像@robmayoff所说的一样。)而且,请这次更加努力地正确格式化它。没有人喜欢过于懒惰以至于无法格式化其代码的海报...
马特

Answers:


65

@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它的属性已经存在。


6
您是否尝试过编译?首先,它是lazy没有@在当前版本的Xcode,即使这样我仍然得到同样的错误消息。
Martin R

@MartinR好的电话。全部固定!
哑光

@matt我仍然无法看到该更改的错误:'ViewController -> () -> ViewController!' does not have a member named 'corX'
Mike S

2
@MikeS听起来就像您键入它,而不是复制和粘贴。你一定不要遗漏任何东西。例如,如果省略UIImageView类型声明,则会得到该错误。或者,如果您省略了懒惰的声明,则会收到该错误。这都是必要的。直接从浏览器复制并粘贴到代码中。在当前版本的Xcode(Xcode 6 GM)中,它可以正常编译。
马特

3
@matt,您的指示无效,请接受它。只有pkamb讲述的是应该得到满足,以去除编译器错误3个条件:lazy.self: Type。尽管您曾经用过,: Type但并没有说这很重要。而且您的答案更长,更难阅读。我认为它有更多的支持是不公平的。
凯林

10

您的依附财产必须是:

  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

我正在解决问题的标题:

直到对象初始化之后知道属性的初始值时,惰性属性和计算属性都可以帮助您处理。但是有一些区别。我用粗体突出显示了差异。

如果您只需要在初始化一些其他变量后初始化一个变量,则应该使用lazyie,如果重点是简单地添加延迟(以便所有必需的属性在此之前初始化),那么使用惰性是正确的方法它。

但是,如果您需要不断地基于另一个变量来更改变量,则需要一个可以同时起作用的计算属性:

  • 如果计算出的属性集,则将变量设置为其相关的存储属性
  • 如果存储的属性已设置(或再次重置),则将触发随后计算的属性的更改。

如果更改惰性属性的值,则不会影响其基于的存储属性。看这里


使用惰性属性的一个很好的例子是,一旦拥有firstNamelastName之后,您就会懒惰地实例化a,fullName并且可能永远都不会更改对象的firstName lastName,而fullName仅是一次性的……或者也许只能做些事情通过lazy属性,直到您不访问该属性,它都不会被初始化,因此这将减少类的初始化负担。正在加载大量计算。

此外,lazy还会使用will信号通知其他开发人员:“嘿,先阅读一下其他属性并了解它们是什么...然后进入这个懒惰属性...因为此属性的值基于它们+这可能很沉重不应该太早访问的计算...”

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

结果,计算属性将增加额外的计算...如果您的计算非常简单且调用不频繁,则无需担心,但是如果调用过于频繁或占用大量CPU,则可能会更好考虑其他选择...


-1
//
//  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()

这个答案如何应用于这个问题?
pkamb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.