Swift属性

时间:2020-02-23 14:44:03  来源:igfitidea点击:

在本教程中,我们将讨论Swift属性。
属性是Swift中类,结构和枚举的重要组成部分。

Swift属性

Swift属性大致分为两种类型。

  • 存储的属性:存储常量和变量,由类和结构提供
  • 计算属性:计算值而不是存储值。
    由类结构和枚举提供。

注意:计算所得的属性不能为常数。

Swift存储属性示例

下面给出了一个存储属性的例子。

struct Rect{
  
  var length : Int
  let breadth : Int
  
}

var r = Rect(length: 5, breadth: 5)
r.length = 6

长度和宽度都是上述代码段中存储的属性(分别为变量和常数)。

修改常量结构实例

let r1 = Rect(length: 5, breadth: 5)
r1.length = 6 //compile-time error.

如果将结构初始化为常量,则即使将其声明为变量,也无法更改存储的属性。
原因是结构是值类型。
由于类是引用类型,因此情况并非如此。

Swift Lazy属性

根据Apple的文档,Swift Lazy属性定义为:

与其他属性不同,惰性属性是在首次访问之前初始化的。

  • 当防止不必要的对象创建并随后节省内存时,惰性属性很有用。

  • 通常,当某个属性依赖于其他未知属性时,它被定义为惰性。

  • 惰性修饰符不能添加到常量。
    常数在初始化完成之前需要一个初始值,而懒惰属性则并非如此。

  • 惰性属性必须具有初始化程序,不能简单地与任何变量一起使用。

以下是lazy var的错误用法情况。

struct Rect{
  
  lazy var length : Int //compile-time error. lazy properties require an initaliser.
  let breadth : Int
  
}

下面是lazy var的示例:

truct Rect{
  
  var length : Int
  let breadth : Int
  
  init(length : Int, breadth: Int) {
      print("Rect struct is initialised now from the lazy var property")
      self.length = length
      self.breadth = breadth
  }
  
}

struct Square{
  
  var sidesEqual : Bool
  
  lazy var r = Rect(length: 6, breadth: 6)
  
  init(sidesEqual : Bool) {
      self.sidesEqual = sidesEqual
  }
}

var s = Square(sidesEqual: false)

if s.sidesEqual
{
  print(s.r.length)
}
else{
  print("Rect struct hasn't been initialised using the lazy var")  //this gets printed
}

var s1 = Square(sidesEqual: true)

if s1.sidesEqual
{
  print(s1.r.length) //prints Rect struct is initialised now from the lazy var property \n 6
}
else{
  print("Rect struct hasn't been initialised using the lazy var") //not printed
}

从上面的代码中可以明显看出,只有在满足条件时才从惰性var实例实例化Rect结构。

当代码中有许多对象实例时,惰性属性非常方便,因为只有在需要时才创建声明的实例。

一旦初始化了惰性属性,为了进行进一步的访问,它通常会重用第一个实例。

注意:如果标记有lazy修饰符的属性同时被多个线程访问,并且该属性尚未初始化,则不能保证该属性仅被初始化一次。

闭包的Swift Lazy属性

class Name {
  
  var name : String?
  lazy var greet : String = {[weak self] in
      
      guard let s = self else { return "Unable to unwrap self"}
      guard let n = s.name else { return "No name found" }
      
      return "Hi, \(n)"
     
  }()
  
  init(name: String) {
      self.name = name
  }
  
}

var n = Name(name: "Anupam")
print(n.greet) //prints "Hi, Anupam\n"

重要事项

  • 在上面的代码中,我们在lazy var属性内定义了一个闭包。

  • 闭包返回一个字符串。

  • 为了消除强大的参考周期,我们捕获了一个"弱自我"。

  • " guard let"用于可选的展开。

让我们对上面的代码进行一些修改,看看它的行为。

var n = Name(name: "Anupam")
print(n.greet) //prints "Hi, Anupam"
n.name = nil
print(n.greet) //prints "Hi, Anupam"

上面的代码片段是一个有趣的案例,它表明lazy var属性每次都被重用。
name属性的更改没有任何作用。

var n = Name(name: "Anupam")
n.name = nil
print(n.greet) //prints "No name found"

在以上代码段中,第二个保护let语句未能解开可选字符串。

Swift计算属性

与"存储的"不同,"计算的属性"不存储值。
而是将它们用作获取程序和可选的设置程序,以间接检索和设置其他属性和值。
下面给出一个基本示例。

struct Rect{
  
  var length : Double
  let breadth : Double
  
  var area : Double {
      
      get{
          return length*breadth
      }
      set(newArea)
      {
          length = newArea/breadth
      }
  }
}

var r = Rect(length: 6, breadth: 5)
print(r.area) //prints 30.0
r.area = 40
print(r.length) //prints 8.0

在上面的代码中,将getter和setter用作计算属性" area"上的" get {}"和" set(param_name){}"。
使用点语法访问getter和setter。

如果未在设置器中指定参数名称,则Swift会将默认名称分配为" newValue"。

struct Rect{
  
  var length : Double
  let breadth : Double
  
  var area : Double {
      
      get{
          return length*breadth
      }
      set
      {
          length = newValue/breadth
      }
  }
}

无法将计算属性分配为惰性var属性。
定义了get和set的计算属性不能设置为常量" let"。

只读计算属性

不带setter的Compulated属性是只读的计算属性。
它们可以定义为常量。

struct Rect{
  
  var length : Double
  let breadth : Double
  
  var area : Double {
      get{
          return length*breadth
      }
  }
}

var r = Rect(length: 6, breadth: 5)
r.area = 50 //compile-time error. area is a get-only property.

在上述情况下,我们也可以放开get关键字:

var area : Double {
          return length*breadth
  }

Swift属性观察员

Swift属性观察者响应属性值的更改。
这些属性通常在两个属性值相互依赖时使用。
它们包含两种方法:

  • willSet:这将在存储值之前触发。
    它允许我们在更改之前读取旧值。
    我们可以使用关键字newValue访问新值。

  • didSet:在存储值后触发。
    它使我们能够读取旧值和新值。
    我们可以使用关键字" oldValue"访问旧值

每次设置该值都会触发属性观察器。
让我们在需要将码转换为英寸的示例中使用属性观察器。

struct yardToInchesConversion{
  
  var yard : Double = 0 {
      willSet{
          print("new value of yards \(newValue)")
      }
      didSet{
          print("old value of yards \(oldValue)")
          inches = yard*36
          print("Updated value of inches \(inches)")
      }
  }
  var inches : Double = 0
}

var yi = yardToInchesConversion()
yi.yard = 22

//The Following gets printed on the console:
new value of yards 22.0
old value of yards 0.0
Updated value of inches 792.0

Swift的全局和本地属性

全局变量是在任何函数,方法,闭包或者类型上下文之外定义的变量。
局部变量是在函数,方法或者闭包上下文中定义的变量。
全局常量和变量始终是惰性计算的,无需使用惰性修饰符进行标记。

Swift类型属性

  • 类型属性用于类型(类/结构/枚举),而不是该类型的实例。

  • 类型属性是用关键字" static"定义的。

  • 静态类型属性不能在子类中覆盖。
    在这种情况下,关键字" class"用于计算的属性。

  • 存储的属性不支持class关键字。

以下代码段清楚地说明了上述概念。

class A {
  
  static var i : Int = 5
  static var name : String {
      return "Hello World"
  }

  class var multiplyByANumber : Int {
      return i*5
  }
  
  //class var j : Int = 1   //不支持. Won't compile.
  static func printI()
  {
      print("Value of i is \(i)")
  }
  class func appendClassName()
  {
      print("Class A Hello World")
  }
  
}

class SubClass : A {
  
  //static var i = 10    Won't Compile
  override class var multiplyByANumber : Int{
      return i*5*5
  }
  
  override class func appendClassName(){
      print("Class SubClass Hello World")
  }
  
}

以上程序的输出:

Class A Hello World
25
Class SubClass Hello World
125

Swift下标

Swift下标是访问集合,列表或者序列的成员元素的快捷方式。
要访问数组,字典或者列表中的元素,我们使用array [index],dictionary [key]等形式。
类似地,我们可以为任何类型定义下标。

我们可以为同一类型定义多个下标,并根据传递给下标的索引值的类型来选择要使用的适当下标重载。

下标的语法类似于如下所示的计算属性。

subscript(index: Int) -> Int {
  get {
      //return an appropriate subscript value here
  }
  set(newValue) {
      //perform a suitable setting action here
  }
}

下标的示例实现如下所示。

class M {
  private var month = ["Jan", "Feb", "March", "April"]
  subscript(index: Int) -> String {
      get {
          return month[index]
      }
      set(newValue) {
          self.month[index] = newValue
      }
  }
}
var m = M()
m[3] //April 
m[0] = "Dec"