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


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


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,如果重点是简单地添加延迟(以便所有必需的属性在此之前初始化),那么使用惰性是正确的方法它。

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

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

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


使用惰性属性的一个很好的例子是,一旦拥有firstName&lastName之后,您就会懒惰地实例化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.