## Swift 屬性和方法
在Swift中,構建結構體或者類屬性和方法中,它們分為:存儲型屬性、計算型屬性和靜態屬性。
### 計算型屬性
```
struct Point {
var x = 0.0 // 存儲型屬性
var y = 0.0
}
struct Size {
var width = 0.0 // 存儲型屬性
var height = 0.0
}
class Rectangle {
var origin = Point() // 存儲型屬性
var size = Size()
// 計算性屬性,必須聲明稱變量,因為它的值是根據其他屬性的值計算得出。
// 另外,計算型屬性必須聲明數據類型,否則將會報錯 (error: computed property must have an explicit type)
var center: Point {
// getter
get {
let centerX = origin.x + size.width / 2
let centerY = origin.y + size.height / 2
return Point(x: centerX , y: centerY)
}
// setter
set {
origin.x = newValue.x - size.width / 2 // newValue 是要傳遞給center計算性屬性的新值
origin.y = newValue.y - size.height / 2
}
}
// 計算性屬性,只有 getter 即只讀屬性(無setter)。
var area: Double {
return size.width * size.height
}
init (origin: Point , size: Size){
self.origin = origin
self.size = size
}
}
var rect = Rectangle( origin: Point(), size: Size(width: 10 , height: 5) )
rect.center
rect.origin = Point(x: 10 , y: 10)
print( rect.center ) // 改變計算型屬性值
rect.center = Point()
rect
```
### 靜態屬性
使用 `static` 關鍵字聲明靜態屬性,該屬性定義在類型屬性上的Type Property。靜態屬性存儲在類型中,不是存儲在類的實例化對象上
```
class Player {
var name: String // 存儲型屬性
var score: UInt32 = 0
static var highestScore: UInt32 = 0 // 使用關鍵字static聲明靜態屬性,存儲最高分
init(name: String) { // 構造函數中給存儲型屬性賦值
self.name = name
}
func play() {
let score = arc4random() % 100
print("\(self.name) played and got \(score) scores.")
self.score += score
print("Total score of \(self.name) is \(self.score)")
if self.score > Player.highestScore { // 使用類自身調用靜態變量,而不是使用self調用自己的靜態屬性并且也不能省略Player的書寫
Player.highestScore = self.score
}
print("Highest scores is \(Player.highestScore)")
}
}
let player1 = Player(name: "Player1")
let player2 = Player(name: "Player2")
player1.play() // 玩一局得分
player1.play() // 玩第二局得分
player2.play()
player2.play()
player2.play()
```
### 類型方法 靜態方法
定義在整個類型上的靜態方法。
```
struct Matrix{
var m: [[Int]]
var row: Int
var col: Int
init?(_ arr2d: [[Int]]){
guard arr2d.count > 0 else{
return nil
}
let row = arr2d.count
let col = arr2d[0].count
for i in 1..<row{
if arr2d[i].count != col{
return nil
}
}
self.m = arr2d
self.row = row
self.col = col
}
func printMatrix() {
for i in 0 ..< row {
for j in 0 ..< col {
print(m[i][j],terminator:"\t")
}
print()
}
}
static func identityMatrix(n: Int) -> Matrix? { // 靜態方法
if n <= 0 {
return nil
}
var arr2d:[[Int]] = []
for i in 0 ..< n {
var row = [Int](repeating: 0, count: n)
row[i] = 1
arr2d.append(row)
}
return Matrix(arr2d)!
}
}
if let m = Matrix([[1,2,],[3,4]]){
m.printMatrix()
}
if let e = Matrix.identityMatrix(n: 8) {
e.printMatrix()
}
```
### 屬性觀察器
如果外部修改了類中的成員屬性操作類的 `static` 靜態屬性大小,可以通過關鍵字限制。可以通過關鍵字 `didSet`、`willSet` 進行邏輯判斷。
```
class LightBulb{
static let maxCurrent = 30
var current = 0 {
willSet { // 在對象中賦值 current 之前,{} 中的邏輯代碼將執行
print("Current value changed. The Change is \(abs(current - newValue))")
}
didSet{ // 在對象中賦值 current 完成,{} 中的邏輯代碼將執行
if self.current == LightBulb.maxCurrent {
print("Pay attention, the current value get to the maximum point.")
}else if self.current > LightBulb.maxCurrent{
print("Current too height , falling back to previous setting.")
self.current = oldValue
}
print("The current is \(self.current)")
}
}
}
let bulb = LightBulb()
bulb.current = 20
bulb.current = 30
bulb.current = 40
```
> 屬性觀察器經常應用在保護數據是否合法的場景中。
> 注意: `didSet` 和 `willSet` 不會在初始化階段調用。
如下Demo:
```
enum Theme {
case DayMode
case NightMode
}
class UI{
var fontColor: UIColor!
var backgroundColor: UIColor!
var themeMode: Theme = .DayMode {
didSet{
switch themeMode {
case .DayMode:
fontColor = UIColor.black
backgroundColor = UIColor.white
case.NightMode:
fontColor = UIColor.white
backgroundColor = UIColor.black
}
}
}
init(themeMode: Theme) {
self.themeMode = themeMode
}
}
let ui = UI(themeMode: Theme.DayMode)
ui.themeMode
ui.fontColor // nil
ui.backgroundColor // nil
```
我們查看到 `ui.fontColor` 和 `ui.backgroundColor` 的值為 `nil` 。說明在執行初始化的時候并沒有調用 `didSet` ,應該進行如下代碼改進。
```
enum Theme {
case DayMode
case NightMode
}
class UI{
var fontColor: UIColor!
var backgroundColor: UIColor!
var themeMode: Theme = .DayMode {
didSet{
self.chengeTheme(themeMode: themeMode)
}
}
init(themeMode: Theme) {
self.themeMode = themeMode
self.chengeTheme(themeMode: themeMode)
}
func chengeTheme(themeMode: Theme) {
switch themeMode {
case .DayMode:
fontColor = UIColor.black
backgroundColor = UIColor.white
case.NightMode:
fontColor = UIColor.white
backgroundColor = UIColor.black
}
}
}
let ui = UI(themeMode: Theme.DayMode)
ui.themeMode
ui.fontColor // nil
ui.backgroundColor // nil
```
### 惰性加載(延遲加載)
很多時候我們有這種需求,當我們有一個大計算量的屬性需要計算,我們會將邏輯放在構造函數中,可能使用這個計算型屬性的情況不多。每次實例化都需要重新計算這個屬性,這樣會導致性能的浪費。
我們也可以把這個計算邏輯放入到計算型屬性中,那假如用戶經常調用的話就會一次又一次的重新計算。
為了調節這中矛盾。Swift為我們發明了一種 Lazy Property ,延遲屬性。
```
class ClosedRange {
let start: Int
let end: Int
var width: Int{
return end - start + 1
}
lazy var sum: Int = { // 計算型延遲加載
var res = 0
for i in self.start ... self.end{
res += i
}
return res
}() // 延遲性屬性閉包的調用
init?( start: Int , end: Int ) {
if start > end {
return nil
}
self.start = start
self.end = end
}
}
if let range = ClosedRange(start: 0, end: 10_000){
range.width
range.sum // 第一次調用會計算
range.sum // 重復調用不會多次計算屬性值
range.sum
}
```
> `lazy` 關鍵字不允許使用在常量屬性上。
#### 惰性加載的一些其他場景
```
// 根據經緯度計算地理位置
class Location {
let latitude: Double
let longitude: Double
lazy var address: String? = {
return nil
}()
init(latitude: Double , longitude: Double){
self.latitude = latitude
self.longitude = longitude
}
}
// 圖書、電影、音樂相關App
class Book {
let name: String
lazy var content: String? = {
// 從本地讀取書的內容
return nil
}()
init(name: String){
self.name = name
}
}
// Web請求
class Web {
let url: String
lazy var html: String? = {
// 從網絡讀取url對應的html
return nil
}()
init(url: String) {
self.url = url
}
}
```
### 訪問控制
Swift 的訪問控制是通過**文件**為單位控制的。其中:
* public 可以被模塊外訪問
* internal 可以被本模塊訪問,當我們不顯式指定的時候,所有的類、屬性或者方法的訪問權限都是 **internal** 。
* private 可以被本文件訪問
例如:`Sources` 目錄下 `Ui.swift` 文件內容如下:
```
enum Theme {
case DayMode
case NightMode
}
class UI{
private var fontColor: UIColor!
private var backgroundColor: UIColor!
var themeMode: Theme = .DayMode {
didSet{
self.chengeTheme(themeMode: themeMode)
}
}
init(){
self.themeMode = .DayMode
self.chengeTheme(themeMode: self.themeMode)
}
init(themeMode: Theme) {
self.themeMode = themeMode
self.chengeTheme(themeMode: themeMode)
}
private func chengeTheme(themeMode: Theme) {
switch themeMode {
case .DayMode:
fontColor = UIColor.black
backgroundColor = UIColor.white
case .NightMode:
fontColor = UIColor.white
backgroundColor = UIColor.black
}
}
func show() {
print("The font color is \(self.fontColor == UIColor.white ? "BLACK" : "WHITE")")
print("The background color is \(self.backgroundColor == UIColor.black ? "WHITE" : "BLACK")")
}
}
```
`Sources` 目錄下 `App.swift` 文件中定義相關的結構或者類,內容如下:
```
import Foundation
public class App{
private let ui = UI()
public var name: String
public init(name: String) {
self.name = name
}
public func switchMode() {
switch ui.themeMode {
case Theme.DayMode:
ui.themeMode = Theme.NightMode
case Theme.NightMode:
ui.themeMode = Theme.DayMode
}
}
public func show() {
print("The App name is \(self.name)")
ui.show()
}
}
```
### 單例模式
有兩個文件,`gameManager.swift` 是一個單例類。
```
import Foundation
public class GameManager {
public var score = 0
public static let defaltGameManager = GameManager() // 自己初始化自己
private init(){
}
public func addScore(){
self.score += 10
}
}
```
> 注意: 初始化函數設置為 `private` ,`defaultGameManager` 設置為靜態常量。
```
import UIKit
let gameManager = GameManager.defaltGameManager // 不允許實例化 GameManager,只能通過GameManager的靜態屬性 defaltGameManager 獲得 GameManager 的實例
gameManager.addScore()
gameManager.score // 10
let gm = GameManager.defaltGameManager
gm.addScore()
gm.score // 20
```
- 學習筆記
- 基礎
- 基本類型之整型
- 基本類型之浮點型
- 基本類型之布爾類型以及簡單的 if 語句
- 基礎類型之元組
- 基本類型之其他
- 運算符
- 基礎運算符
- 比較運算符、邏輯運算符
- 三元運算符
- 范圍運算符for-in
- 邏輯控制
- 循環結構
- 選擇結構
- 字符串
- Character和Unicode
- String.index 和 range
- 可選型
- 容器類
- 數組初始化
- 數組基本操作
- 字典初始化
- 字典基本操作
- 集合初始化
- 集合基本操作
- 函數
- 閉包
- 枚舉
- 結構體
- 類
- 文檔注釋
- 屬性和方法
- 下標和運算符重載
- 拓展和泛型
- 協議
- 其他
- Swift 3.0 For 循環
- Swift 隨機數的生成
- IOS開發玩轉界面 UIKit
- UILable 文本顯示控件
- UIButton 簡單的交互控件
- UIImageView 圖片控件
- UISearchBar 搜索控件