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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25854300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 05:33:54  来源:igfitidea点击:

How to initialize properties that depend on each other

xcodeswiftframecgrectmake

提问by Lukas K?hl

I want a picture to move to the bottom. If I press a button the pic should move down by 1.

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

I added the picture and a 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)
}

At least I said in function "fahren" to move the picture down by 1.

至少我在函数“fahren”中说将图片向下移动 1。

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

So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.

所以我的问题是:我在使用这些 corX 和 corY 时遇到了几个错误。没有它们,它可以完美运行,但它就像一个一次性按钮。错误是: ViewController.Type 没有名为 corX 的成员,并且 ViewController.Type 没有成员名 panzer 在那里我得到了我犯的错误 // 以显示在哪些行中。

PS: I use Xcode Beta5

PS:我使用 Xcode Beta5

Here's the complete code without anything else:

这是没有任何其他内容的完整代码:

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

采纳答案by matt

@MartinR has pointed out the major issue here:

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

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

The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corXand self.corY- but there is no selfbecause selfis exactly what we are in the middle of creating.

问题是 Swift 默认初始化器不能引用另一个属性的值,因为在初始化时,该属性还不存在(因为实例本身还不存在)。基本上,在panzer的默认初始值设定项中,您隐式地引用了self.corXand self.corY- 但没有,self因为self这正是我们正在创建的内容。

One workaround is to make the initializer lazy:

一种解决方法是使初始化程序惰性:

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

That's legal because panzerdoesn't get initialized until later, when it is first referred to by your actual code. By that time, selfand its properties exist.

这是合法的,因为panzer直到稍后才被初始化,当它第一次被您的实际代码引用时。到那个时候,self它的属性已经存在了。

回答by pkamb

Your dependent property needs to be:

您的从属财产必须是:

  1. lazy
  2. Have an explicit : Type
  3. Use self.to access other properties
  1. lazy
  2. 有明确的 : Type
  3. 使用self.访问其他属性

Example:

例子:

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'

回答by Honey

I'm addressing the title of the question:

我正在解决问题的标题:

Both lazy and computed properties help you deal with when the initial value for a property is notknown until after the object is initialized. But there are some differences. I've highlighted the differences with bold.

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

If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazyie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.

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

But if you need to constantlychange a variable based on another, then you need a computed property that would work both ways:

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

  • if the computed property setthen it sets the variables its related stored properties
  • if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
  • 如果设置了计算属性,则它会设置与其相关的存储属性的变量
  • 如果设置了存储的属性(或再次重置),那么它将触发计算属性的更改。

if you change the lazy property's value it won't affect the storied properties that it was based on. see here

如果您更改惰性属性的值,它不会影响它所基于的故事属性。看这里



A good example for using a lazy property would be that once you have firstName& lastNamethen you would lazily instantiate a fullNameand likely you would never change the firstName lastName of your object your fullName is a onetime only... Or perhaps something that can only be done by lazyproperties is that up until you don't access the property it won't ever get initialized, therefore this would decreasethe initialization load of your class. Loading a heavycalculation.

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

Additionally using the lazywill signalto other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."

另外使用lazywill信号给其他开发人员:“嘿,首先阅读其他属性并了解它们是什么......然后来到这个懒惰的属性......因为它的价值是基于它们+这可能是一个沉重的不应过早访问的计算......”

As for computed property a good example would be if you set the temperature to Fahrenheitthen you also want your celsiustemperature to change its value...and if you set the celsiustemperature then again you want to change your Fahrenheitvalue.

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

As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...

因此,计算属性会增加额外的计算……如果您的计算非常简单且调用频率不高,则无需担心,但如果调用过于频繁或非常消耗 CPU,那么它可能会更好想想其他选择……

回答by CrazyPro007

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