Swift中属性和变量有什么区别?

6

从最初的一些教程中,我看到属性属于一个类,实质上是C++世界中使用的“全局变量”(多年前编写的)。我还将变量视为更多的“局部”实体,仅在方法中使用/存储信息。

然后我看到了这个Quora主题:https://www.quora.com/Apple-Swift-programming-language/What-is-the-difference-between-a-property-and-a-variable现在我看到属性能够执行与其调用相关联的代码。这很酷,但也为我打开了一堆其他问题。

是否有其他简单明了的方法来记住属性和变量之间的区别?

3个回答

12

属性属于对象,而变量不属于对象。 可以声明一个变量,而无需与特定类或其他对象相关联。 属性必须与特定对象(即:类、枚举或结构)相关联。


1
谢谢!我理解了。仍在努力理解父类变量在子类对象方法中的确切含义。当子类对象覆盖父类方法时,它可以更改相应局部变量的值,但仍不能影响父类中该变量的值。我的理解正确吗? - Madhu Chamarty
1
是的,在子类中覆盖方法的变量不会影响父类中的变量。 - 123

6
局部变量只是你所使用的内容。你可以完全控制这些内容,如果你在函数中更改了一个变量,就不会有任何外部内容知道它的变化。如果我编写了一个框架,而你使用它,如果我决定更改函数的局部变量,那么使用我的框架的应用程序将继续运行,就好像什么也没有改变一样。
另一方面,类描述了一份契约。当你使用一个类时,你可以访问到他们公开宣传的所有内容。这意味着,如果我编写了一个框架,你使用它,如果我更改或删除了类的某个公共成员,那么如果你以前正在使用这个成员,你的代码将会出错。
因此,在许多语言中,将实例变量标记为公共的是不好的做法。实例变量没有附加的逻辑,如果在某个时间点我想要在字段更改时触发某些操作,或者如果我想要完全删除该字段(并代之以子对象中的值),那么我就必须更改公共契约(例如将字段转换为一对get/set方法),这可能会破坏你的代码。
为此,Swift 让属性作为一个间接性质。Swift 属性在很大程度上可以视为愚昧的值,但如果你需要将存储的值更改为计算的值或其他内容,则可以在不更改类界面的情况下执行该操作。这样,你就不会破坏依赖于属性的现有代码。

2
谢谢!这让我很有感触 - 我从你的回答中学到了新概念,即获取/设置方法以及属性的计算值与存储值状态。我正在学习的教程还没有深入探讨属性。要点:属性可能很棘手,因为它们是“全局”的,而变量更容易控制。 - Madhu Chamarty
1
对于你的“Takeaway point”,我有一个小想法,即属性总是全局的。但这并非总是如此,因为如果我使用访问修饰符“private”声明属性,则其作用域限制在类的范围内,不再是“全局”的。或者这样做会将其转换为变量。不正确吧。你怎么看? - G.Abhisek
1
@G.Abhisek,我不理解你的问题。我不确定哪里说属性总是全局的。另外,在类级别上,变量和属性之间没有区别,因此您无法将属性“转换”为变量。 - zneak
1
@zneak 抱歉,我是在回复Madhu Chamarty对你的回答的要点的评论。 - G.Abhisek
嗯,他可能还没有涉及访问控制的重点。 - zneak

4

Swift变量、常量和属性

[Swift类型]

variable - 命名存储地址的存储单元。每个变量都有一个类型,该类型定义了内存大小、属性和行为。

Swift变量和常量

constant 是一个 variable,但是在定义之后不能被修改。

//definition
var <name> = <initial_value>
//type annotation
var <name>: <Swift_type> [= <initial_value>] // [] is optional

//var - variable
var myVariable1 = 11 
var myVariable2: Int 
myVariable2 = 12

//let - constant
let myConstant1 = 21 
let myConstant2: Int 
myConstant2 = 22

全局变量和局部变量

全局变量是在函数、类外定义的变量。 局部变量是指在类型上下文(类、结构体、枚举)、函数或函数参数内定义的变量。[关于]

属性

属性 - 将值与类型上下文关联起来。 它由变量和绑定的getter/setter组成。 它具有字段语法,但在底层使用方法(getter/setter)。

存储属性和计算属性

它们可以属于实例(实例属性)或类型(类型属性):
存储属性 (class, structure)
计算属性 (class, structure, enum)

存储属性 是一种局部变量->类型上下文内的变量。 Swift存储属性不支持像Objective-C那样的 实例变量

  • 变量存储属性 - var
  • 常量存储属性 - let

它支持属性观察器 (willSet, didSet)

计算属性 - 提供getter和可选的setter以每次计算一个值。

public class Person {
    var firstName = "John"
    var lastName = "Wick"
    
    var fullNameComputedProperty: String {
        get {
            return "\(firstName) \(lastName)"
        }
        
        //optional
        set {
            let arr = newValue.split(separator: " ")
            firstName = String(arr[0])
            lastName = String(arr[1])
        }
    }
    
    var addressStoredProperty: String {
        //Property Observers
        willSet {
            print("old address:\(addressStoredProperty)")
            print("new address:\(newValue)")
            //addressStoredProperty is not updated yet
        }
        didSet {
            print("old address:\(oldValue)")
            print("new address:\(addressStoredProperty)")
        }
    }
}

惰性存储属性

当第一次访问到它时计算属性的值(按需计算)

  • 只能使用var lazy,因为let需要在初始化时确定一个值。

使用closure初始化/定制存储属性

官方文档

你可以通过帮助closure来初始化/设置/定制存储属性。

  • 在闭包末尾加上()会立即执行闭包并将值分配给存储属性(计算并返回一个值)。
  • 在初始化情况下,无法访问任何实例变量或函数,因为它还没有初始化。
  • 在初始化情况下,它将为每个对象仅执行一次,如果使用静态,则为类执行一次。[示例]

示例

func testStoredPropertyWithClosure() {
    class ClassA { }
    
    class ClassB {
        static let staticStoredProperty: ClassA = {
            //is called only when you access to it like ClassB.staticStoredProperty
            print("init staticStoredProperty")
            return ClassA()
        }()
        
        var storedProperty: ClassA = {
            print("init storedProperty")
            //self.foo() //Error: Class declaration cannot close over value 'self' defined in outer scope
            return ClassA()
        }()
        
        func foo () {
            storedProperty = {
                print("customize storedProperty")
                return ClassA()
            }()
        }
    }
    
    let b = ClassB()
    b.foo()
    
    ClassB.staticStoredProperty
}

闭包存储属性 vs 计算属性

  • 闭包存储属性只被调用一次,并且在初始化后可以更改(如果是var
  • 计算属性每次被调用时都会重新计算

[Java变量、属性...]


来自C#背景,字段和属性的主要区别在于您可以获取字段的地址并通过引用传递它(以便其他代码可以直接写入它),而这是无法使用属性实现的。但在Swift中(至少对我来说),不清楚_variable stored property_何时表示可直接寻址的字段:在您的第一个示例中,我假设语法var firstName =“ John”表示可以通过引用传递的字段,但是如果您想要一个简单的可变属性,消费者不能像字段一样直接引用它,该怎么办? - Dai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接