GeekBand Swift高級(jí)編程(第二周)

結(jié)構(gòu)與枚舉

認(rèn)識(shí)結(jié)構(gòu)(struct)
struct屬于值類(lèi)型,具有拷貝語(yǔ)義(賦值和傳參)
struct不支持面向?qū)ο螅饕糜诙x輕量級(jí)數(shù)值類(lèi)型;class支持面向?qū)ο螅饕糜谠O(shè)計(jì)有豐富關(guān)系的組件系統(tǒng)(有繼承、多態(tài)等復(fù)雜設(shè)計(jì)模式)。
struct有傳參拷貝成本,不要定義尺寸過(guò)大的結(jié)構(gòu);class有ARC內(nèi)存管理成本。
不要再struct內(nèi)定義引用類(lèi)型屬性(會(huì)改變struct的值拷貝語(yǔ)義)。

class RPoint{
    var x:Int
    var y:Int
    
    init(x:Int, y:Int){   //class的初始化器,必須寫(xiě)。
        self.x=x
        self.y=y
    }
    
    deinit{
        print("clean up")
    }  
}

struct SPoint{
    var x:Int
    var y:Int
    var z:RPoint    
}

var rp=RPoint(x:10, y:20)

//struct有默認(rèn)按成員初始化器
var sp1=SPoint(x:10, y:20, z: RPoint(x: 100,y: 200))

var sp2=sp1
print(" \(sp1.x), \(sp1.y), \(sp1.z.x), \(sp1.z.y)")     //" 10, 20, 100, 200\n"
print(" \(sp2.x), \(sp2.y), \(sp2.z.x), \(sp2.z.y)")     //" 10, 20, 100, 200\n"
sp1.x+=10
sp1.y+=10
sp1.z.x+=10
sp1.z.y+=10

print(" \(sp1.x), \(sp1.y), \(sp1.z.x), \(sp1.z.y)")     //" 20, 30, 110, 210\n"
print(" \(sp2.x), \(sp2.y), \(sp2.z.x), \(sp2.z.y)")     //" 10, 20, 110, 210\n"

在struct中定義一個(gè)class

struct VS class
相同點(diǎn):
都可以定義一下成員:屬性、方法、下標(biāo)、初始化器。
都支持類(lèi)型擴(kuò)展、協(xié)議。
不同點(diǎn):
類(lèi)支持繼承和多態(tài),結(jié)構(gòu)不支持。
類(lèi)必須自己定義初始化器,結(jié)構(gòu)會(huì)有默認(rèn)的按成員初始化器
類(lèi)支持析構(gòu)器,結(jié)構(gòu)不支持
類(lèi)的實(shí)例在對(duì)上,由ARC負(fù)責(zé)釋放;結(jié)構(gòu)的實(shí)例在棧上,棧結(jié)束會(huì)自動(dòng)釋放,不參與ARC管理。
類(lèi)支持引用相等比較(===與!==),結(jié)構(gòu)不支持

認(rèn)識(shí)枚舉(enum)
enum用于定義一組相關(guān)的值成員,enum同屬于值類(lèi)型,具有拷貝語(yǔ)義(賦值與傳參)。

//定義枚舉類(lèi)型,換行每一行都要用case
enum Color {
      case Red
      case Green
      case Blue
}

//或者可以不換行,用一個(gè)case就可以
enum ComplexColor {
      case Red,Green,Blue,Alpha
}

//枚舉實(shí)例的使用
var c1 = Color.Red
var c2:Color
c2 = .Green  //也可以寫(xiě)成  c2 = Color.Green

可以使用switch-case語(yǔ)句處理enum,但case必須包含所有的枚舉值,否則需要用default。

func print (color: Color) {
        switch color  {
        case .Red:
                print("Red Color!")
        case .Green:
                print("Green Color")
       }
}

可以為enum指定原始值,即raw value, 類(lèi)型可以是字符、字符串、整數(shù)、或者浮點(diǎn)數(shù)。數(shù)值默認(rèn)從0開(kāi)始,字符串與枚舉值名稱(chēng)相同。

//指定原始類(lèi)型。默認(rèn)從0開(kāi)始,這里設(shè)置Monday = 1那么就從1開(kāi)始了
enum WeekDay: Int {
      case Monday = 1, Tuesday, Wednesday, Thursday, Friday, Satutday, Sunday
}

//用原生值來(lái)初始化,rawValue是默認(rèn)值
var day: Weekday?
day = WeekDay(rawValue: 7)
//獲取一個(gè)rawValue
var data = WeekDay.Saturday.rawValue

enum支持關(guān)鍵值,可以設(shè)置不同類(lèi)型的值成員,類(lèi)似聯(lián)合數(shù)據(jù)結(jié)構(gòu)

//枚舉關(guān)聯(lián)值
class Point {
      var x=0
      var y=0
      init(x:Int, y:Int) {
          self.x = x
          self.y = y
      }
}

enum Position {
      case Number(Point)
      case Name(String)
}

var p1 = Position.Number(Point(x: 123, y: 588))
var p2 = Position.Name("Shanghai People's Square")

//不同類(lèi)型的值成員枚舉類(lèi)型的使用,根據(jù)不同的類(lèi)型做出不同的操作
func print(position: Position) {
      switch position {
      case .Number (let number):
          print("[\(number.x), \(number.y)]")
      case .Name (let name):
          print(name)
      }
}

enum還可以定義以下成員:計(jì)算屬性、方法、初始化器。

//定義計(jì)算屬性、方法、初始化器
enum Sex {
      case Male
      case Female
//初始化器
      init? (symbol: Character) {
            switch symbol {
            case "M"
                self = .Male
            case "F"
                self = .Female
            default:
                return nil
            }
      }

    stactic var Default: Sex {
          return Sex.Male
    }

    func show () {
          switch self {
          case Male:
              print("I am Male")
          case Female:
              print("I am Female")
          }
    }
}

var sex = Sex.Female
sex.show()

var defaultSex = Sex.Default

var newSex = Sex(symbol: "F")

繼承與多態(tài)

Inheritance
繼承:子類(lèi)自動(dòng)繼承基類(lèi)的屬性、方法、下標(biāo)
只有類(lèi)支持繼承,結(jié)構(gòu)和枚舉不支持繼承
繼承同時(shí)支持實(shí)例成員和類(lèi)型成員

class Shape{
    var no=0
    
    func move() {
        print("NO: \(no) Shape.move")
    }
}

class Rectangle: Shape{
    var leftUp=Point()
    var width=0
    var height=0   
}

繼承的兩層含義:
成員復(fù)用:子類(lèi)復(fù)用基類(lèi)成員

var r1=Rectangle()
var c1=Circle()

//成員復(fù)用
r1.no++
r1.move()

c1.no++
c1.move()

類(lèi)型抽象:將子類(lèi)當(dāng)作父類(lèi)來(lái)使用(IS-A關(guān)系準(zhǔn)則)

func process(shape: Shape){
    shape.no++
    shape.move()
}

var r1=Rectangle()
var c1=Circle()

//類(lèi)型抽象
process(c1)
process(r1)

var s:Shape
s=c1
s=r1

繼承的內(nèi)存模型

class Base{
    var x=10
    var y=20
    
    static var min=1000
    
    func invoke(){
        print("Base.invoke")
    }
}

/*
func invoke(self:Base){
    print("Base.invoke")
}*/

class Sub: Base{
    var z=30
    static var max=2000
}

var b=Base()
var s=Sub()

print(Base.min)   //1000
print(Sub.min)    //1000 這句等于print(Base.min)

Base.min++

print(Base.min)   //1001
print(Sub.min)    //1001

b.invoke()// invoke(self: b)
s.invoke()// invoke(self: s)
繼承的內(nèi)存模型

認(rèn)識(shí)多態(tài) Polymorphism
多態(tài):子類(lèi)在同一行為接口下,表現(xiàn)不同的實(shí)現(xiàn)方式。
子類(lèi)使用override關(guān)鍵字來(lái)表達(dá)多態(tài)定義。
可以重寫(xiě)的成員有:方法、屬性、下標(biāo);包括實(shí)例成員和類(lèi)型成員。
在子類(lèi)代碼中,可以使用super來(lái)調(diào)用基類(lèi)的實(shí)現(xiàn)

class Shape{
    var no=0

    func move() {
        print("Shape.move")
    }
}

class Rectangle: Shape{
    
    override var no: Int {
        get{
            print("Rectangle.no.get()")
            return super.no
        }
        set{
            print("Rectangle.no.set()")
            super.no=newValue
        }
    }

    override func move() {
        print("Rectangle.move")
    }    
}

在成員上使用final阻止子類(lèi)override該成員;在類(lèi)上使用final阻止該類(lèi)被繼承

class Shape{
    var no=0
    
    //func show()不能被子類(lèi)重寫(xiě)
    final func show(){
        print("Shape.show")
    }
    
    //0x000640
    func move() {
        print("Shape.move")
    }
}
vTable的內(nèi)存模型
func process(shape: Shape){
    shape.no++
    shape.move() //根據(jù)實(shí)際類(lèi)型來(lái)調(diào)用
}

//變量有雙重身份:
//1. 聲明類(lèi)型
//2. 實(shí)際類(lèi)型
var sp: Shape
sp=Shape()
sp.move() // JMP  GetVirtualMethod(sp) 二次指針間接運(yùn)算
sp.show() // JMP  0x000340

sp=Rectangle()
sp.move() // JMP  GetVirtualMethod(sp) 二次指針間接運(yùn)算
process(sp)

sp=Circle()
sp.move()
process(sp)

繼承中的init和deinit
初始化器 init
如果子類(lèi)沒(méi)有定義初始化器,則將自動(dòng)繼承基類(lèi)的初始化器
如果子類(lèi)定義了初始化器,則不再繼承,此時(shí)子類(lèi)初始化器必須調(diào)用基類(lèi)的一個(gè)初始化器。如果手工不調(diào)用,編譯器將自動(dòng)生成調(diào)用
如果子類(lèi)初始化器與基類(lèi)初始化器原型一致,必須使用override
在子類(lèi)中使用基類(lèi)屬性,必須確保首先調(diào)用基類(lèi)初始化器

析構(gòu)器 deinit
如果子類(lèi)沒(méi)有定義析構(gòu)器,會(huì)自動(dòng)繼承基類(lèi)析構(gòu)器
子類(lèi)析構(gòu)器執(zhí)行完畢后,會(huì)自動(dòng)調(diào)用基類(lèi)析構(gòu)器(無(wú)法手工調(diào)用)
子類(lèi)析構(gòu)器自動(dòng)具有多態(tài)性


class Base{
    var data1:Int
    init(){
        data1=100
        print("\(data1)")
        print("Base.init")
    }
    
    deinit{
        print("Base.deinit")
    }
}

class Sub: Base{
    var data2=200
     override init(){
        super.init()
        print("\(data1), \(data2)")
        print("Sub.init")
    }
    deinit{
        print("Sub.deinit")
    }
}

func process(){
    var b:Base
    b=Base()
    print("----------")
    
    var s:Base
    s=Sub()
    print("----------")
}

process()

協(xié)議

認(rèn)識(shí)協(xié)議 Protocol
協(xié)議:類(lèi)型的合同約定,只描述外部接口,不提供具體實(shí)現(xiàn)
協(xié)議可以包含以下成員:屬性、方法、初始化器、下標(biāo)、操作符
一個(gè)類(lèi)型可以實(shí)現(xiàn)多個(gè)協(xié)議,協(xié)議可以應(yīng)用在如下類(lèi)型上。但可以添加class關(guān)鍵字標(biāo)明協(xié)議只能應(yīng)用在類(lèi)上:類(lèi)、結(jié)構(gòu)、枚舉

//定義協(xié)議
protocol Drawable{
    
    var discription: String{
        get
    }
    
    func draw()
    
    init()

    
    subscript(index: Int) -> Int {
        get
    }
    
    func ==(lhs: Self, rhs: Self) -> Bool
}

//實(shí)現(xiàn)協(xié)議
class Point: Drawable{
    var x:Int
    var y:Int
    
    required init(){
        x=10
        y=10
    }
    
    var discription: String{
        return "[\(x), \(y)]";  
    }

    func draw(){
        print(self.discription)
    }
    
    subscript(index: Int) -> Int {
        return 0
    }
}

使用協(xié)議
協(xié)議本質(zhì)上是一種類(lèi)型,可以作為聲明類(lèi)型,但不能創(chuàng)建實(shí)例
協(xié)議變量的內(nèi)存模型遵從實(shí)際類(lèi)型的內(nèi)存模型
引用類(lèi)型傳參、拷貝采用傳引用方式
值類(lèi)型轉(zhuǎn)參、拷貝采用傳值方式
檢查協(xié)議類(lèi)型
使用is檢查類(lèi)型是否實(shí)現(xiàn)了協(xié)議
使用as?和as!將類(lèi)型下溯轉(zhuǎn)型為協(xié)議

protocol AProtocol{
    func display()
}

class Base{
    var no=0
}

class Sub:Base,AProtocol{
    
    func display(){
        print(no)
    }
}

//Compile-time Type 編譯時(shí)類(lèi)型,聲明類(lèi)型
var item1, item2:Base

//Runtime Type 運(yùn)行時(shí)類(lèi)型,實(shí)際類(lèi)型
item1=Base()
item2=Sub()

if(item1 is AProtocol){
    print("the runtime type of item1 conforms Shape protocol")
}

if(item2 is AProtocol){
    print("the runtime type of item2 conforms Shape protocol")
}

var item3:AProtocol?
var item4=Sub()
item3 = item1 as? AProtocol
item3 = item2 as? AProtocol

item3=item4

協(xié)議中的屬性
協(xié)議中可以定義只讀屬性,也可以定義讀寫(xiě)屬性
協(xié)議中可以定義實(shí)例屬性,也可以定義類(lèi)型屬性
協(xié)議中只能定義變量屬性,不能定義常量屬性
實(shí)現(xiàn)協(xié)議時(shí),并不要求實(shí)現(xiàn)為存儲(chǔ)屬性,還是計(jì)算屬性

協(xié)議中的方法
協(xié)議可以定義實(shí)例方法、也可以定義類(lèi)型方法
協(xié)議中的方法不能定義參數(shù)的默認(rèn)值
針對(duì)值類(lèi)型的mutating協(xié)議方法
值類(lèi)型(struct和enum)實(shí)現(xiàn)的實(shí)例方法如果要更改實(shí)例本身,需要在協(xié)議方法的定義中標(biāo)明mutating關(guān)鍵字,同時(shí)在方法實(shí)現(xiàn)時(shí)也添加mutating關(guān)鍵字
添加了mutating的協(xié)議方法,對(duì)類(lèi)的實(shí)現(xiàn)方法無(wú)影響。在類(lèi)內(nèi)實(shí)現(xiàn)mutating協(xié)議方法時(shí),無(wú)需再添加mutating關(guān)鍵字

協(xié)議中的初始化器
協(xié)議中可以定義初始化器,但不可以定義析構(gòu)器
當(dāng)class中實(shí)現(xiàn)協(xié)議定義的初始化器時(shí),需要添加required關(guān)鍵字
標(biāo)明子類(lèi)也需要提供該初始化器
如果定義為final類(lèi),則不需要required關(guān)鍵字
協(xié)議可以定義可失敗的初始化器init?,具體實(shí)現(xiàn)時(shí)可以失敗,也可以非失敗

更多協(xié)議形式
協(xié)議繼承
一個(gè)協(xié)議可以繼承一個(gè)或多個(gè)協(xié)議
實(shí)現(xiàn)自協(xié)議的類(lèi)型,可必須實(shí)現(xiàn)父協(xié)議中約定的成員
協(xié)議組合
可以使用protocol<A,B,...>來(lái)組合多個(gè)協(xié)議
實(shí)現(xiàn)組合協(xié)議的類(lèi)型,必須實(shí)現(xiàn)組合協(xié)議中的每一個(gè)協(xié)議
組合協(xié)議是一個(gè)臨時(shí)類(lèi)型
可選協(xié)議
協(xié)議的某些成員可以定義為optional,不必實(shí)現(xiàn)
可選協(xié)議只能應(yīng)用于class,不能應(yīng)用于struct和enum
可選協(xié)議必須標(biāo)明@objc特性(即使不需要和Objective-C互操作)

protocol AProtocol{
    func display()
}

//協(xié)議繼承
protocol BProtocol: AProtocol{
    func invoke()
}

protocol CProtocol{
    func excute()
}

class ClassA:BProtocol{
    
    func display(){
        print("display")
    }
    func invoke(){
        print("invoke")
    } 
}

class ClassB:AProtocol,CProtocol{
    func display(){
        print("display")
    }
    
    func excute(){
        print("excute")
    }
}


//協(xié)議組合
func process( item: protocol<AProtocol,CProtocol>){
    
    item.display()
    item.excute()   
}

var b=ClassB()
process(b)

//可選協(xié)議
@objc protocol DProtocol {
    optional var discription: String { get }
    func move()
    
}

class ClassC: DProtocol{
    
    @objc func move(){    
    }
}

字符串

認(rèn)識(shí)字符串 String
String是一個(gè)Unicode編碼、16位字符的字符序列
String與NSString支持無(wú)縫互操作
String被定義為struct,值類(lèi)型,拷貝時(shí)具有值語(yǔ)義
String是一個(gè)struct,但內(nèi)部包含一個(gè)指向堆上的“內(nèi)容指針”,其指向的對(duì)象存放真正的字符串內(nèi)容。

String的內(nèi)存模型

使用字符串
使用var和let來(lái)控制字符串的可變性

var str1 = "Hello"
let str2 = ",Swift"

獲取子串中的字符

//枚舉字符
for item in str1.characters{
    print(item)
}

for index in str1.characters.indices {
    print(str1[index])
}

print(str1[str1.startIndex])
//print(str1[str1.endIndex])
print(str1[str1.startIndex.successor()])
print(str1[str1.endIndex.predecessor()])
let index = str1.startIndex.advancedBy(4)
print(str1[index])

使用append和+鏈接字符串

//連接字符串
str1.appendContentsOf(",World")

var str3=str1+str2

str1+=str2

var char:Character="!"
str1.append(char)

使用字符轉(zhuǎn)義

//字符轉(zhuǎn)義
let heart = "\u{2665}"
print(heart)
let words = "\tHello"
print(words)

字符串插值(String Interpolation)

//字符串插值
var x=100
var y=200.0
var text="[\(x),\(y)]"

copy-on-write 共享技術(shù)
同一個(gè)字符串內(nèi)容拷貝到不同的變量中時(shí),“內(nèi)容指針”不變,即不同變量“共享”同一份堆內(nèi)存。從而實(shí)現(xiàn)“節(jié)省內(nèi)存”。
如果某一個(gè)變量的字符串內(nèi)容改變時(shí),先將原來(lái)堆內(nèi)存拷貝一份,“內(nèi)容指針”指向新的拷貝,然后再更改新的拷貝。從而確?!罢_性”。
copy-on-write的目的是實(shí)現(xiàn)“內(nèi)容相同的字符串共享內(nèi)存,同時(shí)又支持字符串隨時(shí)改變”。
最佳實(shí)踐:盡量不改變字符串,盡量使用常量字符串let

copy-on-write更改前

copy-on-write更改后

緩存容量與增長(zhǎng)
字符串初始化后,會(huì)分配一個(gè)緩存容量capacity,其長(zhǎng)度一般大于實(shí)際的字符數(shù)量
當(dāng)字符串增長(zhǎng)時(shí),如果實(shí)際需求大于capacity,其capacity會(huì)以二倍的方式指數(shù)增長(zhǎng)。伴隨的代價(jià):
分配新的堆內(nèi)存2*capacity
將原來(lái)堆內(nèi)存上的內(nèi)容拷貝到新內(nèi)存
釋放原來(lái)堆內(nèi)存
最佳實(shí)踐:估計(jì)好capacity,預(yù)先分配好一定容量,避免以后capacity的增長(zhǎng)
str.reserveCapacity(10000) //分配了10000個(gè)字節(jié)給capacity

集合類(lèi)型

集合類(lèi)型有數(shù)組(Array),集合(Set),字典(Dictionary)三種。
認(rèn)識(shí)數(shù)組
Array是一個(gè)有序的元素序列,支持隨機(jī)存取,支持動(dòng)態(tài)更新長(zhǎng)度。
索引從0開(kāi)始,索引訪問(wèn)越界會(huì)拋出運(yùn)行時(shí)異常。
Array被定義為Struct,值類(lèi)型,拷貝時(shí)具有值語(yǔ)義。
Array是一個(gè)Struct,但內(nèi)部包含一個(gè)指向堆上的“元素指針”,其指向的對(duì)象存放真正的數(shù)組元素。
如果數(shù)組元素為值類(lèi)型,拷貝數(shù)組時(shí),元素包含值本身。
如果數(shù)組元素為引用類(lèi)型,拷貝數(shù)組是,元素引用指向相同的對(duì)象。

數(shù)組的內(nèi)存模型

使用數(shù)組
使var和let來(lái)控制數(shù)組的常量性:數(shù)組長(zhǎng)度和元素內(nèi)容都不能更改。

//變量數(shù)組和常量數(shù)組
var array5=[1,2,3]
let array6=[1,2,3]

數(shù)組遍歷
使用for循環(huán)訪問(wèn)array[index]需要檢查索引越界,具有性能代價(jià)
盡可能使用for-in遍歷數(shù)組元素;或使用Array.enumerate()遍歷索引;二者編譯器會(huì)優(yōu)化掉索引檢查
盡量避免使用insert和remove操作,因?yàn)闀?huì)改變數(shù)組元素序列,涉及到大量的內(nèi)存拷貝操作,代價(jià)較高。

//數(shù)組操作
for item in array5{
    print(item)
}

for(index, value) in array5.enumerate(){
    print("\(index): \(value)")
}

for index in 0..<array5.count {
    print(array5[index])
}

for var index=0;index<array5.count;index++ {
    print(array5[index])
}

緩存容量與增長(zhǎng)
數(shù)組初始化后,會(huì)分配一個(gè)緩存容量capacity,其長(zhǎng)度一般大于實(shí)際的元素?cái)?shù)量
當(dāng)數(shù)組長(zhǎng)度增加時(shí),如果實(shí)際需求大于capacity,其capacity會(huì)以近似二倍的方式指數(shù)增長(zhǎng)。伴隨的代價(jià):
分配新的堆內(nèi)存2*capacity
將原來(lái)堆內(nèi)存上的元素拷貝到新內(nèi)存
釋放原來(lái)的堆內(nèi)存
最佳實(shí)踐:估計(jì)好capacity,預(yù)先分配好一定容量,避免以后capacity的增長(zhǎng)
copy-on-write 共享技術(shù):與String的實(shí)現(xiàn)原理一樣,詳情請(qǐng)看String的copy-on-write 共享技術(shù)。

認(rèn)識(shí)集合
Set是一個(gè)無(wú)序的集合,其存儲(chǔ)的值不能重復(fù)
Set中的元素必須有哈希值,即支持Hashable協(xié)議
Set被定義為Struct,值類(lèi)型,拷貝時(shí)具有值語(yǔ)義
Set也是一個(gè)Struct,但內(nèi)部包含一個(gè)指向堆上的“元素指針”,其指向的對(duì)象存放真正的元素。

var words = Set<String>()

words.insert("Hello")
words.insert("Swift")
words.insert("Language")
words.insert("Swift")

認(rèn)知字典
Dictionary是一個(gè)存儲(chǔ)key-value的無(wú)序的集合,key唯一,value可重復(fù)
Dictionary中的key必須有哈希值,即支持Hashable協(xié)議
Dictionary被定義為Struct,值類(lèi)型,拷貝時(shí)具有值語(yǔ)義
Dictionary也是一個(gè)Struct,但內(nèi)部包含一個(gè)指向堆上的“元素指針”,其指向的對(duì)象存放真正的元素。

var dictionary1 = [String:Int]()
var dictionary2 : Dictionary<String,Int>
dictionary2=["Jason":36, "Tom":31, "Marty":44]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容