Swift學習進階

1.print(str)

  • print函數能夠將表達式的結果輸出到控制檯,類似C的printf函數和OC的NSLog函數。
  • print函數定義:
public func print(_ items: Any..., //任意個數,任何類型的參數它們將輸出到控制檯
 separator: String = default,//輸出多個參數之間的分隔符
  terminator: String = default)//輸出字符串之後的結束符號

參數separatorterminator都可以省略,separator只有字輸出數據大於1時纔有意義。

  • 示例:
print("a","b","c","d","e", separator: "|", terminator: "-")

打印結果爲a|b|c|d|e-,用|將打印的字母分開,用-來結束打印。

  • 自定義打印日誌:
//自定義log p28
func hjLog(mes:Any){
    print("file:\(#file) column:\(#column)  line:\(#line) \(mes)")
}

由於Swift中不能定義宏,只能定義一個打印日誌的方法,上面方法打印文件路徑,行數,及要打印的內容,可以方便的定位到打印的位置。

2.浮點型取餘

  • Swift中允許浮點型取餘,但是在Swift3之後取餘運算符%不能應用於浮點數運算,需要使用public func truncatingRemainder(dividingBy other: Double) -> Double方法來計算浮點型取餘。
var c = 19.22
//浮點型取餘 
let d = c.truncatingRemainder(dividingBy: 6.1)

3.Swift中的數據類型

  • 根據這些類型在賦值或給函數傳遞時的方式,可分爲值類型引用類型值類型就是創建一個副本,把副本賦值或傳遞過去,這樣在函數的調用過程中不會影響原始數據;引用類型就是把數據本身的引用(即指針)賦值或傳遞過去在函數的調用過程中會影響原始數據。
  • Swift中整型、浮點型、布爾型、字符型、字符串、元組、集合、枚舉。結構體值類型,而類屬於引用類型。如Swift中String是值類型、NSString是引用類型。
  • 整型、浮點型、布爾型、字符型、字符串、元組、集合等類型本質上都是結構體類型,結構體有構造函數,通過構造函數創建並初始化實例。
  • 用於判斷的幾種方式:
    1. ==比較基本類型的值是否相等
    2. ===比較對象類型,是不是同一個對象
    3. is判斷某個實例是否爲某種類型
    4. as強制類型轉換

4.Swift中的for循環

  • Swift3之後C語言風格的for語句不再使用, Swift3之後for語句只能與in關鍵字結合使用。
  • 示例1:只需要知道連續區間的值(只需要循環變量)
//打印1到9平方的值
for i in 1..<10{
    print("\(i)x\(i)=\(i*i)")
}
  • 示例2:只需要連續獲取集合元素的值(不需要循環變量)
//打印集合元素
let nums = [1,2,3,4,5,6,7,8,9]
for item in nums {
    print(item)
}
  • 示例3:既需要集合元素的值,又需要腳標(需要循環變量),可以使用結合的enumerated()方法
let nums = [1,2,3,4,5,6,7,8,9]
for(index,item) in nums.enumerated(){
    print("\(index):\(item)")
}
  • Swift中的區間分爲兩種,全閉合區間...和左閉右開區間..<

5. break語句

  • break語句可用於循環語句結構,(Swift中的循環語句有while、repeat-while、for ),作用是強制退出循環結構,不執行循環結構中的剩餘語句。由於Swift的選擇switch默認會添加break,所以一般不需要收到添加break,但添加上也不會出問題。
for i in 1...10{
    print("\(i)x\(i)=\(i*i)")
    if i == 5{
        break;
    }
}
  • break可以配合標籤使用
label1: for x in 0..<5 {
    label2: for y in (1...5).reversed(){
        if x == y {
            break label1
        }
        print("(x,y)=(\(x),\(y))")
    }
}

默認情況下break只會跳出最近的內循環,而示例中條件成立時直接跳出循環,給外循環添加了一個標籤label1,然後break後面指定這個標籤,當條件成立就跳出該標籤的外循環。reversed()是反向變量區間。

6.Set集合

  • Swift集合有數組、字典、Set集合數組是一組有序的由相同類型元素構成的集合;字典由兩部分組成,一個鍵集合,一個值集合,鍵集合不能有重複元素,而值集合可以重複,鍵值成對出現;Set集合是由一串無序的,不能重複的相同類型元素構成的集合。
  • 與OC任何對象類型不同,三種集合都強調是元素的類型一致,但這裏的類型指的是泛型。如果想包含任何類型可指定爲Any
//正規聲明
var arr : Array<Any> = [1,2,"3",1.22]
print(arr[1])

//簡寫聲明
let dic:[AnyHashable : Any] = [1:2,2:3,"3":4,4:"5"]
//字典的key和value都爲任何類型,有可能是可選類型,獲取元素值時要解包
print(dic[1]!)

let set:Set<String> = ["1","12","123"]
print(set.first!)
  • Array和Dictionary有簡寫的聲明,而Set集合沒有簡寫的聲明。因爲Set和Array唯一的區別是無序且不重複(當不考慮敘述而且沒有重複的元素時,二者可互相替換),如果簡寫了就無法區分是Set還是Array了。
  • 三種集合強調的點不同, Array強調有序;Set強調不重複(無序);字典強調key唯一(不能重複),通過key取值,也是無序的。
  • Set的一般操作:
let set:Set<String> = ["1","123","12","12","1234"]
print("第一個元素\(set.first!)")
print("元素個數\(set.count)")
var set2:Set<String> = ["1234","1","123","12"]
if set == set2{
    print("set等於set2")
}
//插入一個元素
set2.insert("插入")
//刪除某個元素
let item = "1"
set2.remove(item)
print(set2)
//刪除一個元素,這裏並不是第一個元素,而是隨機的
set2.removeFirst()
print(set2)
//判斷是否包含某個元素
if set.contains("1234"){
    print("set有該元素")
}

Set一般操作
結果可以看出Set的first方法獲取的並不一定是第一個元素,而是隨機的。多個重複的元素在Set中只算一個,從count可以看出。

  • Set集合遍歷
for item in set{
    print(item)
}

for (index,item) in set.enumerated(){
    print("\(index+1):\(item)")
}

Set遍歷
注意:Set的enumerated()方法可以取出Set的索引和元素, (index,item)是元組類型。這裏的index是循環遍量,可以表示循環次數,而不是元素的序號腳標。

  • Set集合間的運算,首先了解一下幾個概念(A,B是兩個Set集合):
  1. 交集:屬於A且屬於B的元素集合
  2. 並集:屬於A或屬於B的元素集合
  3. 異或集合:A與B的並集元素集合去掉A與B的交集集合中的元素後剩下元素的集合
  4. 差集:屬於A而不屬於B的元素集合稱A與B的差集。(A與B的並集去掉B中所有元素後的集合)
  5. 子集:B中所有元素都A的元素,那麼久稱B是A的子集。但是Set集合運算過程中不涉及Set子集概念。
let A:Set<String> = ["a","b","c","d"]
let B:Set<String> = ["c","d","e","f"]
print("A與B的交集 = \(A.intersection(B))")
print("A與B的並集 = \(A.union(B))")
print("A與B異或集合 = \(A.symmetricDifference(B))")
let C = A.subtracting(B)
print("A與B差集 = \(C)")
if C.isSubset(of: A){
   print("C是A的子集")
}

Set集合間的操作

7.函數

7.1 .Swift 函數參數
  • Swift中的函數參數很靈活,具體體現在傳遞參數有多種形式。
  • 我們可以爲每個參數提供標籤,爲調用者說明參數的含義,這些變遷命名應該唯一,並且有意義:
   func rectangleArea(W width:Double,H height:Double) ->Double{
   return width*height
}
print(rectangleArea(W: 10.0, H: 10.0))

W,H就是參數標籤,外部調用時會提示使用。

  • 如果定義函數沒有聲明標籤,原則上也是可以的
func rectangleArea2(width:Double,height:Double) ->Double{
  return width*height
}

print(rectangleArea2(width: 10.0, height: 10.0))
  • 省略參數標籤,在Swift3以後,調用函數時要求指定所有參數的標籤,除非函數定義是使用下劃線_關鍵字聲明的標籤。
func rectangleArea3(_ width:Double,_ height:Double) ->Double{
  return width*height
}
print(rectangleArea3(10,10))
  • 給參數設默認值,當調用時可以忽略該參數,調用時如果沒傳值就取默認值,如果賦值了就會覆蓋掉默認值
func test(a:Int = 10,b:Int) -> Int{
  return a+b
}
print(test(b: 20)) //30
print(test(a: 1, b: 2)) //3
  • 可變參數:參數個數可以變化,調用時可以接受不確定數量的參數,這些參數具有相同的類型,有點像傳入了一個數組
func sum(nums:Int...)->Int{
  var total = 0
  for num in nums{
   total += num
  }
  return total
}
print(sum(nums: 1,2,3))
print(sum(nums: 1,2,3,4,5,6,8))

sum函數是用來求多個整型的函數,參數nums:Int…是Int類型的可變參數,在函數體重nums被認爲是一個Double類型的數組

  • 類型參數的引用傳遞:除了類是引用類型,其他如整型…都是值類型,但有時候我們想在函數內部改變函數外面參數的值,這樣就需要將值類型參數以引用類型方式傳遞。
func increment(value: inout Double,auto:Double = 1.0){
   value += auto
}
var value:Double = 10.0
print(value)
increment(value: &value)
print(value) //11.0

參數value是需要增長的數值,它被設計爲inout類型,inout修飾的參數稱爲輸入輸出參數,value必須是變量不能是let修飾的常量。

7.2. Swift函數返回值
  • 函數返回值分爲無返回值和有返回值,無返回值其類型是void,可以省略不寫;有返回值又分爲單個返回值,和多個返回值,單個返回值就是返回一種類型,多個返回值可以返回多個類型,將這些不同類型的返回值放到元組中返回就可以了。
7.3. Swift函數類型
  • 每個函數都有一個類型,使用函數類型與使用其他數據類型一樣,可以聲明變量或常量,也可以作爲其他函數參數或返回值使用。
  • 用函數類型聲明常量或變量
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
let rectangleArea1: (Double,Double)->Double = rectangleArea(width:height:)
print(rectangleArea1(10,10)) //100.0

var rectangleArea2: (Double,Double)->Double = rectangleArea(width:height:)
rectangleArea2(20,20)
rectangleArea2 = rectangleArea(width:height:)
print(rectangleArea2(30,30)) //900.0
  • 用函數類型作爲函數返回類型使用
//計算矩形面積
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//計算三角形面積
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}

//作爲函數返回類型使用
func getArea(type:String) -> (Double,Double)->Double {
    var returnFunc: (Double,Double)->Double
    switch type {
    case "矩形":
     returnFunc = rectangleArea
    default://三角形
      returnFunc = triangleArea
    }
    return returnFunc
}
let rectangleFunc = getArea(type: "矩形")
print("矩形面積:\(rectangleFunc(20,20))")  //矩形面積:400.0
let triangleFunc = getArea(type: "三角形")
print("三角形面積:\(triangleFunc(20,20))") //三角形面積:200.0

getArea返回值類型爲函數類型,常量rectangleFunctriangleFunc只是接收了getArea函數的返回值,是將計算矩形面積和三角形面積的函數真正聲明瞭,rectangleFunc(20,20)和triangleFunc(20,20)纔是對計算面積的函數的真正調用。總之就是getArea函數調用是爲了聲明rectangleAreatriangleArea,返回的函數常量或變量的調用纔是真正用於計算面積的,

  • 用函數類型做爲函數參數類型使用
//計算矩形面積
func rectangleArea(width: Double,height:Double) -> Double {
    return width * height
}
//計算三角形面積
func triangleArea(width: Double,height:Double) -> Double {
    return width * height/2.0
}
//作爲參數類型使用
func getAreabByFunc(funcName:(Double,Double)->Double,a:Double,b:Double)->Double {
    let area = funcName(a,b) //對傳進來函數參數的真正調用
    return area;
}
print("矩形面積:\(getAreabByFunc(funcName: rectangleArea, a: 10, b: 10))")  //矩形面積:100.0
print("三角形面積:\(getAreabByFunc(funcName: triangleFunc, a: 10, b: 10))") //三角形面積:50.0

getAreabByFunc調用值只需要將函數名傳進來就可以,傳進來的函數參數直接在裏面調用。
7.4. Swift嵌套函數

  • 將函數定義在另外的函數體重,稱爲嵌套函數
//嵌套函數
func calculate(opr:String) -> (Int,Int) -> Int {
    //定義加函數
    func add(a:Int,b:Int) -> Int{
        return a + b
    }
    //定義減函數
    func sub(a:Int,b:Int) -> Int{
        return a - b
    }
    var result: (Int,Int) -> Int
    switch opr {
    case "+":
        result = add
    case "-":
        result = sub
    default:
        result = add
    }
    return result
}
let addfunc = calculate(opr: "+") // 聲明加函數
print("5+5 = \(addfunc(5,5))") //5+5 = 10
let subfunc = calculate(opr: "-")// 聲明減函數
print("5-5 = \(subfunc(5,5))")//5-5 = 0

嵌套函數的作用域在外函數體內,但我們可以定義外函數的返回值類型爲嵌套函數類型,從而將嵌套函數出啊遞給外函數,被其調用者調用。

8.運算符重載

  • Swift中除了class類型是引用類型,其他整型,浮點型,數組,結構體等都是值類型,對於引用類型通常用===!===來判斷是不是同一個對象;對於值類型通常用==!=來判斷兩個值是否相等。
  • 示例:自定義一個機構體,判斷兩個實例是否相等。
struct Student {
    var name = ""
    var no = 0
    var age = 0
}

var stu1 = Student()
stu1.name = "張三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "張三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一個學生")
}else{
    print("不是同一個學生")
}

在這裏插入圖片描述
可以發現在運行時報錯了,報錯說明爲==不能用於兩個Student結構體實例操作。說了stu1和stu2不能用於比較。我們需要在這些類型中重載==!=運算符號。即定義相等規則。

struct Student {
    var name = ""
    var no = 0
    var age = 0
}
//定義重載==號運算符符
func == (lsh:Student,rhs:Student) -> Bool {
    return lsh.name == rhs.name && lsh.no == rhs.no && lsh.age == rhs.age
}
//定義重載!=號運算符符
func != (lsh:Student,rhs:Student) -> Bool {
    return (lsh.name != rhs.name || lsh.no != rhs.no || lsh.age != rhs.age)
}

var stu1 = Student()
stu1.name = "張三"
stu1.no = 1
stu1.age = 18

var stu2 = Student()
stu2.name = "張三"
stu2.no = 1
stu2.age = 18

if stu1 == stu2{
    print("是同一個學生")
}else{
    print("不是同一個學生")
}

在這裏插入圖片描述

9.類型嵌套

  • Swift中的類、結構體和枚舉可以進行嵌套。優點是支持訪問它外部的成員(包括方法、屬性和其他嵌套類型),嵌套可以有多個層次。
class Employee {
    var name = ""
    var no = 0
    var job = ""
    var day = WeekDays.Friday
    var dept = Department()
    
    struct Department {
        var no = 10
        var name = "Sales"
    }
    enum WeekDays{
        case Monday,Tuesday,Wednesday,Thursday,Friday
        
         struct Day {
            static var mes = "Today is ..."
        }
    }
    
    
}
let emplo = Employee()
print(emplo.day)
print(emplo.dept.name)
print(Employee.WeekDays.Day.mes)

10.類和結構體的異同。

  • 相同點:
    1. 定義存儲屬性;
    2. 定義方法;
    3. 定義下標;
    4. 定義構造函數;
    5. 定義擴展;
    6. 實現協議
  • 不同點:(只有類纔有的功能)
    1. 能夠繼承另外一個類;
    2. 能夠覈對運行時對象的類型;
    3. 析構對象釋放資源;
    4. 引用計數允許一個實例有多個引用。
  • 選擇的原則:結構體是值類型,每一個實例沒有獨一無二的標識,而類是引用類型,每一個實例都是獨一無二的標識。
class Employee{ //員工類
  var no = ""
  var name = ""
  var dept: Department?
  
}
struct Department{//部門結構體
  var no = ""
  var name = ""
}

上面示例可以看出,由於員工的編號都是獨一無二,每個員工是獨立的個體,所以員工可以聲明成類Employee;如果具有相同部門標號和部門名稱,我們就認爲是它們是相同的部門,所以就可以把部門設計爲機構體Department。

11.屬性與下標。

  • Swift中的屬性分爲存儲屬性計算屬性。存儲屬性就是oc中的數據成員,計算屬性不存儲數據,但可以通過計算其他屬性返回數據。
class Employee{
  let no = 0
  var firstName = "Tony"
  var lastName = "Guan"
  lazy var dept: Department = Department() //延遲存儲屬性
  var fullName: String{  //計算屬性
      get{
       return firstName + "." + lastName
          
      }
      set(newFullName){
          let names = newFullName.components(separatedBy: ".")
          firstName = names.first!
          lastName = names.last!
      }
//        set{
//            let names = newValue.components(separatedBy: ".")
//            firstName = names.first!
//            lastName = names.last!
//        }
  }
  
}
struct Department{
  var no = ""
  var name = ""
}
let emp = Employee()
//emp.no = 10;編譯會報錯 ,常量屬性不允許被修改。
print(emp.fullName)
emp.fullName = "Jack.Ma"
print(emp.fullName)
let dept = Department()

上面類Employee中的no、firstName、lastName、dept都是存儲屬性,fullName是計算屬性,其中dept是延遲存儲屬性。

  • 延遲存儲屬性:存儲屬性前面加上lazy關鍵字聲明,就是延遲存儲屬性,只在第一次訪問時加載,如果不訪問就不會創建,這樣可以減少內存佔用,注意存儲屬性沒有延遲加載一說,添加到存儲屬性前面會報錯的。
  • 計算屬性:計算屬性本身不存儲數據,而是從其他屬性計算得到數據。計算屬性提供一個Getter(取值訪問器)來獲取值,以及一個可選的(可以不實現set方法)Settter(設置訪問器)來間接設置其他屬性或變量的值,語法如下。
面向對象的類型(classstructenum) 類型名{
存儲屬性
var 計算屬性名: 屬性數據類型{
 get{
 return 計算後的語句值
 }
 set(新屬性值){
 語句組
}
}
}

其中新屬性值是要賦值給屬性值的,當然也可以不寫,Swift提供了一個默認的變量newValue去接收新傳入的值。上面set(newFullName)可以省略如下:

  set{
           let names = newValue.components(separatedBy: ".")
           firstName = names.first!
           lastName = names.last!
       }
  • 只讀計算屬性:計算屬性只有Getter訪問器,沒有Setter訪問器,只能取值而不能賦值。(只讀存儲屬性就是let修飾的存儲屬性)
class Employee{
   let no = 0
   var firstName = "Tony"
   var lastName = "Guan"
   lazy var dept: Department = Department()
   var fullName: String{
       get{
        return firstName + "." + lastName
           
       }
   }
}
let emp = Employee()
//emp.no = 10;
print(emp.fullName)
// emp.fullName = "Jack.Ma" //不能賦值

fullName就是隻讀計算屬性,不能給其賦值,結構體,枚舉的計算屬性也是類似,這裏不再贅述。只讀屬性可以簡化去掉get關鍵字和括號,上面只讀屬性可以簡化爲:

   var fullName: String {
        return firstName + "." + lastName   
       }
  • 存儲屬性觀察者:Swift的屬性觀察者有兩個,willSet:觀察者在修改之前調用,didSet觀察者在修改之後調用 語法格式如下。
面向對象類型(class/struct) 類型名{
   ...
   var 存儲屬性值: 屬性數據類型 = 初始值{
       willSet(新值){
           ...
       }
       didSet(舊值){
           ...
       }
   }
}

示例

class Employee1{
    let no = 0
    var name = "Tony"{
        willSet(newName){
            print("員工新名字:\(newName)")
        }
        didSet(oldName){
            print("員工舊名字:\(oldName)")
        }
    }
}
struct Department1{
    var no = 1{
        willSet{
            print("部門新編號:\(newValue)")
        }
        didSet{
            print("部門舊編號:\(oldValue)")
        }
    }
    var name = "移動開發部"
}

let emp1 = Employee1()
emp1.name = "Jack"
//結構體是值類型,必須用聲明爲變量才能修改其屬性
var dept1 = Department1()
dept1.no = 10

Employee1類中給name存儲屬性添加了觀察者, willSet(newName)中的newName是要傳進來的新值,didSet(oldName)中的oldName是新值傳進來之前的舊值;參數的聲明可以省略。Department1結構體中的no就省略了觀察者參數,Swift提供了對應默認參數,新值默認是newValue,舊值默認是oldValue。這裏需要說明下枚舉只有計算屬性沒有存儲屬性,所以枚舉不支持屬性觀察者

  • 靜態屬性:在屬性前面加static關鍵字,類中也可以加class關鍵字,這樣的屬性稱爲靜態屬性。類,結構體,枚舉都可以定義靜態屬性(也包括靜態存儲屬性和靜態計算屬性)。這裏需要注意的是枚舉中沒有實例存儲屬性但是可以有靜態存儲屬性
struct Account {
    var amount = 0.0 //賬戶金額
    var ower = "" //賬戶名
    static let monthRate = 0.00688 //月利率
    static var yearRate : Double{  //計算存儲屬性不能用let修飾,及不能是常量
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
}
//訪問靜態屬性
print(Account.yearRate)
var account = Account()
//訪問實例屬性
account.amount = 100000.0
print(account.owerInterest)


class Account {
    var amount = 0.0 //賬戶金額
    var ower = "" //賬戶名
    static let monthRate = 0.00688 //月利率
    class var yearRate : Double{  //class換成static子類就不能重寫該屬性
        return monthRate * 12
    }
    var owerInterest:Double{ //年利息
        return Account.yearRate * amount
    }
   static var test:Int = 10{ //靜態屬性也支持添加屬性監聽者
        willSet{
            print(newValue)
        }
        didSet{
            print(oldValue)
        }
    }
}
//訪問靜態屬性
print(Account.yearRate)
var account = Account()
//訪問實例屬性
account.amount = 100000.0
print(account.owerInterest)

class Account2:Account{
    override class var yearRate : Double{  //class換成static子類就不能重寫該屬性
        return monthRate * 12 * 1.1
    }
}

這裏枚舉與結構體類似不再舉例,類中static修飾的屬性爲類的靜態屬性,class修飾的屬性稱爲類的類屬性,區別是類的類屬性可以被子類重寫,但是class不能修飾存儲屬性,static卻可以

  • 實例屬性和靜態屬性總結
    1. 類、結構體,枚舉都支持靜態存儲屬性、靜態計算屬性,實例計算屬性;但是隻有類和結構體支持實例存儲屬性,枚舉不支持實例存儲屬性。
    2. 基於第一條可知只有類和結構體支持添加屬性觀察者(因爲只有存儲屬性才能添加屬性觀察者)
    3. 延遲屬性只能是延遲存儲屬性
    4. 計算屬性必須是var聲明的
    5. let修飾的存儲屬性,前可以加static稱爲靜態存儲屬性,不能加class
    6. class修飾的屬性可以被重寫,但static修飾的屬性不允許被重寫。
    7. class只能修飾計算屬性。
  • 下標:Swift中,我們可以定義一些集合類型,,它們可以回有一些集合類型的存儲屬性,這些屬性可通過下標訪問,其如法格式如下。
面向對象類型(class/struct/enum) 類型名{
   ...
   subscript (參數:參數數據類型)->返回值類型{
       get{
           return 返回值
       }
       set(新屬性值){
           ...
       }
   }
}

Swift中沒有提供二維數組,但是我們可以通過下標自定義一個二維數組。

struct DoubleDimensionalArray {
   let rows:Int,colums:Int
   var grid: [Int]
   init(rows:Int,colums:Int) {
       self.rows = rows //行數
       self.colums = colums //列數
       grid = Array(repeating: 0, count: rows * colums) //初始化數組都爲0
   }
   subscript(row:Int,col:Int) -> Int{
       get{
           return grid[row * colums + col] //返回對應的腳標取出二維數組的值
       }
       set{
           grid[row * colums + col] = newValue //通過腳標給二維數組賦值
       }
   }
}
//初始化一個10行10列的二維數組
var arr = DoubleDimensionalArray(rows: 10, colums: 10)
for i in 0..<10 {
   for j in 0..<10{
       arr[i,j] = i*j //通過腳標給二維數組賦值爲腳標之和
   }
}

for i in 0..<10 {
   for j in 0..<10{
       print("\t \(arr[i,j])",terminator: " ") //通過腳標獲取二維數組中的值
   }
   print("\n")
}

Swift自定義二維數組

11.方法。

  • Swift中,方法是在類,結構體,枚舉中定義的函數,分爲實例方法和靜態方法.
  • 方法和函數的區別:方法是在在類,結構體,枚舉內部定義的.方法調用前面要有主體,而函數就不需要.
  • 我們在枚舉和結構體方法掐面添加關鍵字mutatting,將方法聲明爲可以變方法,可變方法能夠修改值類型變量屬性,但不能修改值類型常量屬性.也就說不可變方法值類型屬性是都不能訪問的,但引用類型的屬性是可以訪問的。
  • static修飾的方法爲靜態方法,當然類中class修飾的方法類方法.與計算屬性類似,實例方法中既可以訪問實例屬性和方法又可以訪問靜態屬性和方法,但是靜態方法不能訪問實例屬性和實例方法,只能訪問靜態屬性和方法.
  • class修飾的方法能被重寫,static修飾的方法不能被重寫.

12.重寫

  • 一個類繼承另一個類的屬性,方法,下標等特徵後,子類可以重寫(override)這些特徵。
  • 重寫實例屬性:實例屬性重寫一方面可以Getter和Setter訪問器,另一方面可以重寫屬性觀察者。
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
}
class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue<8?8:newValue
        }
    }
}

從屬性重寫來看,子類本身並不存儲數據,數據存儲在父類的存儲屬性中,子類將其變成了計算屬性並重寫。
注意:一個屬性重寫了Getter和Setter訪問器後就不能重寫觀察者。另外常量屬性和只讀計算屬性也都不能重寫屬性觀察者。

  • 重寫靜態屬性:
class Account{
    class var staticProp:Double{
        return 0.0668 * 1000000
    }
}
class TermAccount: Account {
    override static var staticProp:Double{
        return 0.0700*1000000
    }
}

Account的靜態屬性staticProp只能用class修飾,因爲要在子類TermAccount重寫該靜態屬性,所以該屬性能被繼承才行,而TermAccount可以用class也可以用static修飾,因爲沒有子類繼承TermAccount的staticProp屬性。

  • 重寫實例靜態方法:
class Person {
    var name: String
    var age: Int
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
    func description() -> String {
        return "\(name)的年齡爲:\(age)"
    }
}

class Student: Person {
    var school: String
    override var age:Int {
        get{
            return super.age
        }
        set{
            super.age = newValue
        }
    }
    override init(name:String,age:Int) {
        self.school = "清華大學"
        super.init(name: name, age: age)
        
    }
    override func description() -> String {
        return "\(name)的年齡爲:\(age),所在學校爲\(school)"
    }
}

靜態方法重寫與實例方法重寫類似,在方法名錢加上override關鍵字即可,但是隻有class修飾的靜態方法才能被被繼承被重寫,static修飾的靜態方法不能被重寫。

  • 下標重寫:對下標重寫也是重寫Getter和Setter訪問器,類似於屬性,這裏不再舉例。
  • final關鍵字:final關鍵字可以聲明類、屬性、方法、和下標。final關鍵字可以聲明類不能被繼承,聲明的屬性、方法和下標不能被重寫。

13.類檢查與轉換(is、as、as!、as?、AnyObject、Any)

  • 使用is進行類型檢查:is操作符可以判斷一個實例是否是某個類的類型。(只要是該類型以及該類型的父類以及父類的父類類型,返回都爲true,類似於oc的isKindOfClass方法)
//Student繼承於Person
let  stu =  Student(name: "馬雲", age: 18)
if stu is Student{
    print("馬雲18歲是一個學生")
}
if stu is Person{
    print("馬雲18歲是一個人")
}

在這裏插入圖片描述

  • 使用as、as!、as?進行類型轉換:
    1. as操作符:用於向上轉型(子類轉換成父類),因爲向上轉型很少進行,所以代碼中很少能夠看到使用as操作符(通常都是向下轉型:父類轉出子類)。
    2. as!操作符: 在類型轉換過程中對可選值進行拆包,轉換結果是費可選類型。將費可選類型轉換爲非可選類型,將可選類型轉換爲非可選類型。
    3. as?操作符: 在類型轉換過程中不進行裁包,轉換結果是可選類型。將非可選類型轉換爲可選類型,將可選類型轉換爲可選類型。
//Student,Worker類都繼承於People
let stu1 = Student(name: "李彥宏", age: 35, school: "北京大學")
let stu2 = Student(name: "馬化騰", age: 45, school: "深圳大學")
let stu3 = Student(name: "馬雲", age: 55, school: "杭州師範大學")
let wk1  = Worker(name: "李開復", age: 56, factory: "微軟")
let wk2  = Worker(name: "張小龍", age: 46, factory: "騰訊")
let people = [stu1,stu2,stu3,wk1,wk2]
for p in people{
    if let stu = p as? Student{ //這裏在是先轉換爲可選類型,並且在轉換成功後進行了可選綁定(因爲都有值就解包了)
        print("Student \(stu.name),年齡:\(stu.age),畢業於:\(stu.school)")
    }else if let wk = p as? Worker{
        print("Worker \(wk.name),年齡:\(wk.age),工作於:\(wk.factory)")
    }
}
let stu4 = people[0]as? Student //這裏是直接賦值爲可選類型
print("--------")
print(stu4 as Any)
print(stu4?.name as Any) //可選類型安全訪問其屬性的寫法
print(stu4!.name)

在這裏插入圖片描述

  • 使用AnyObjectAny類型:Swift提供了兩種類型來表示不確定類型(任意類型),AnyObject表示任何類(class)的類型;Any則表示任何類型,包括Int、Double、Array、struct等基本數據類型,也包括AnyObject類型。也就是說AnyObjectAny的子集。
  • 在OC與Swift混合編程時,OC的id類型和Swift的AnyObject類型可以互換,但是兩者有本質區別。id類型是泛性,可以代表任何指針類型,編譯時編譯器不檢查id類型,是動態的。AnyObject是實實在在表示類的類型,編譯時會檢查AnyObject類型。
  • 原則上若能使用集體的數據類型,則儘量不要使用AnyObject類型,更要少考慮使用Any類型。從集合中取出這些實例時也要儘可能的將AnyObject類型和Any類型轉換爲特定類型,然後再進行接下來的操作。

14.擴展(extension)

  • Swift中可以使用一種擴展機制,在原始類型(類、機構體、枚舉)的基礎上添加新功能。擴展是一種輕量級的繼承機制。
  • Swift中擴展機制可以在原始類型中添加新功能包括:
    1. 實例計算屬性和靜態計算屬性
    2. 實例方法和靜態方法
    3. 構造函數(結構體中可以擴展構造函數;類中只能擴展便利構造函數,不能擴展指定構造函數和析構函數,指定構造函數和析構函數只能在原始類中提供)
    4. 下標
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章