结构体和类(二)

2017-03-26  本文已影响30人  SmartisanBool

结构体和类模块分两篇笔记来学习:

本篇开始学习第二篇类和结构体的内存分析以及何时使用类何时使用结构体,go!

类和结构体的内存分析

swift中大多类型都是值类型,要么是结构体,要么是枚举。他们的内存管理比较简单,因为它们只有一个持有者,它们的内存都是自动的创建和回收,不需要考虑引用计数,也不需要考虑循环引用的问题。举个例子如下:

  struct Person {
    let name: String
    var parents: [Person]
  }
  var john = Person(name: "John", parents: [])
  john.parents = [john]
  print(john)”

示例中当把john加入到数组时,其实他被复制了,并不会引起循环引用的问题,而如果Person声明为类,则会引起循环应用。

_ Swift 的结构体一般被存储在栈上,而非堆上。不过这也有例外,如果结构体的尺寸是动态的,或者结构体太大的话,它还是会被存储到堆上。另外,如果结构体值被函数所持有 (就像用闭包的例子中那样),那么为了持久化,即使程序已经离开了定义该值的作用域,这个值也会被存储在堆上。_

对于类,swift中使用引用计数来进行内存管理,每次创建一个对象的引用,引用计数就会增1,当引用无效时,就会减1,当引用计数为0时,对象就会被系统自动回收。因为有引用计数,所以大多数情况下,我们不需要考虑内存问题,但是也有意外,如下示例:

  //声明一个View类,包含Window属性
  class View {
    var window: Window
    init(window: Window) {
      self.window = window
    }
  }

  //声明一个Window类,包含View属性
  class Window {
    var rootView: View?
  }

  //window引用计数+1
  var window: Window? = Window()
  //window引用计数+1,view引用计数+1
  var view: View? = View(window: window!)
  //view引用计数+1
  window?.rootView = view
  //view引用计数-1
  view = nil
  //window引用计数-1
  window = nil

代码执行完之后,view和window的引用计数都为1。此时两者都互相有着对彼此的强引用,也就产生了引用循环,造成内存泄漏。

如何选择使用类还是结构体?

使用类还是结构体,要根据具体的问题和数据种类来定,接下来根据一个具体的问题,分别使用类、结构体、优化的结构体三种实体类型分别解决一个银行账户转账的实际问题。

首先定义Account类:

    typealias USDCents = Int
    class Account {
      var funds: USDCents = 0
      init(funds: USDCents) {
        self.funds = funds
    }

然后声明俩账户对象:

    let alice = Account(funds: 100)
    let bob = Account(funds: 0)

接着创建一个transfer的转账函数,返回类型为Bool,表明是否转账成功。

    func transfer(amount: USDCents, source: Account, destination: Account)-> Bool{
      guard source.funds >= amount else { return false }
      source.funds -= amount
      destination.funds += amount
      return true
    }

需求看似已解决,但是这种解决方式不是线程安全的,并发线程有可能造成账户莫名的增加或者减少余额。

但是 transfer 函数就要复杂一些,它依然接受一个数值和两个帐号。我们使用 var 来创建输入帐号参数的复制,这样它们就能够在函数内部进行更改了。不过,这样的变更不会改变传进来的原来的值。为了将更改过的帐号信息回传给调用者,我们将返回一个更新过的帐号的多元组,如果转账没有成功,我们返回 nil。

    func transfer(amount: USDCents, source: Account, destination: Account)-> (source: Account, destination: Account)?{
      guard source.funds >= amount else { return nil }
      var newSource = source
      var newDestination = destination
      newSource.funds -= amount
      newDestination.funds += amount
      return (newSource, newDestination)
    }

因为结构体是值,我们知道一个帐号不会被另外的线程更改。至少,这两个帐号的状态都是稳定的。
上一篇 下一篇

猜你喜欢

热点阅读