[Swift] The Swift Programming Language - 初始化/ARC/可選鏈/協(xié)議/拓展/泛型/運(yùn)算符

Initialization

swift的init和OC的不太一樣,是不用return一個(gè)self的~ 它的職責(zé)只是確保這個(gè)類把所有該做的事情先做了再被使用~

存儲(chǔ)屬性應(yīng)該在init里面賦值,或者開始聲明的時(shí)候就給個(gè)default值,這些都是不會(huì)觸發(fā)observer的~

If a property always takes the same initial value, provide a default value rather than setting a value within an initializer.

init是可以加參數(shù)的哦~

struct Color {
    var red = 0.0, green = 0.0, blue = 0.0
    init(red: Double, green: Double, blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }
}

如果所有屬性聲明的時(shí)候都有default值(除了optional的屬性,optional的屬性自動(dòng)默認(rèn)值是nil),并又沒有父類,那么swift會(huì)自動(dòng)生成init方法~

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

對(duì)于值類型會(huì)有的默認(rèn)的init方法,但是如果你自己寫了init就不會(huì)自動(dòng)生成了哦~

struct SomeStructure {
    var storedTypeProperty : String
    
    init() {
        storedTypeProperty = "hh"
    }
}
// error
SomeStructure(storedTypeProperty: "ggg")

如果你想有自己的init又想要默認(rèn)的,可以加extension~

struct SomeStructure {
    var storedTypeProperty : String
}

extension SomeStructure {
    init() {
        storedTypeProperty = "hh"
    }
}
SomeStructure(storedTypeProperty: "ggg")
  • 這個(gè)引發(fā)了我一個(gè)思考,如果子類繼承父類并且覆寫了init,也沒初始化父類的參數(shù)會(huì)怎樣呢?
class Counter {
    var count: Int
    
    init() {
        count = 10
    }
}

class Countera : Counter {
    var counta: Int
    override init() {
        counta = 11
    }
}

print("coutera: \(Countera().count)")
// 輸出為coutera: 10

所以其實(shí)即使你沒有調(diào)用super.init,好像是自動(dòng)會(huì)在子類init結(jié)束后被調(diào)用的~ 關(guān)于父類子類的self問題還可以看看這個(gè):https://segmentfault.com/q/1010000005807384

子類繼承父類的時(shí)候,需要確保子類init也有初始化父類的屬性,Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value. These are known as designated initializers and convenience initializers.

Designated initializers是類似全能初始化,會(huì)init所有屬性,并且調(diào)用super.init(),Convenience initializers是可以少提供幾個(gè)參數(shù),調(diào)用全能初始化然后有些參數(shù)可以用默認(rèn)的值。

  • Designated initializers must call a designated initializer from their immediate superclass.

  • Convenience initializers must call another initializer available in the same class.

  • Convenience initializers must ultimately end up calling a designated initializer.

初始化init的調(diào)用規(guī)則

注意調(diào)用順序哦,防止被其他的初始化函數(shù)改了之前改過的屬性。只有完全初始化完(所有屬性有值)以后才可以調(diào)用方法之類的~

Unlike subclasses in Objective-C, Swift subclasses do not not inherit their superclass initializers by default.

Designated和Convenience的初始化函數(shù)是醬紫寫噠:

// Designated
init( parameters ) {
    // statements
}

// Convenience
convenience init( parameters ) {
    // statements
}

如果子類和父類有一個(gè)同名的convenience init,并且提供了它比父類多的屬性的賦值,那么也就是說父類的其他依賴于這個(gè)convenience init的convenience方法都可以做到初始化子類的所有屬性,也就自然會(huì)被繼承,所以可以用這些父類的convenience init來初始化子類,因?yàn)闀?huì)優(yōu)先調(diào)用子類自己實(shí)現(xiàn)的convenience初始化方法

當(dāng)子類有新增的屬性并聲明時(shí)賦值了default值,并且沒有自己的init方法,那么會(huì)繼承父類所有的Designated以及Convenience的初始化方法。

屬性的default值還可以用closure來定義,初始化的時(shí)候會(huì)執(zhí)行這個(gè)closure得到默認(rèn)值,注意這個(gè)時(shí)候self還沒有init完成,不能在closure里面用self的屬性和方法

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure // someValue must be of the same type as SomeType return someValue
    }()
}

注意到closure賦值給屬性作為default的時(shí)候,最后需要加(),因?yàn)槿绻悴患?,其?shí)就是定義了一個(gè)類型是closure的var,()的含義是調(diào)用也就是立即執(zhí)行。所以這里作為default值需要執(zhí)行得到真正的默認(rèn)值。

舉個(gè)例子:

struct Checkerboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...10 {
            for j in 1...10 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
}

Deinitialization

deinit在對(duì)象銷毀之前調(diào)用,并且只有class有這個(gè)方法。

Superclass deinitializers are inherited by their subclasses, and the superclass deinitializer is called automatically at the end of a subclass deinitializer implementation. Superclass deinitializers are always called, even if a subclass does not provide its own deinitializer. 父類的銷毀函數(shù)會(huì)自動(dòng)調(diào)用的~ 和dealloc類似~


Automatic Reference Counting

Reference counting only applies to instances of classes. Structures and enumerations are value types, not reference types, and are not stored and passed by reference. 值類型其實(shí)沒必要引用計(jì)數(shù)的

swift提供兩種解決循環(huán)依賴的方式:unowned references以及weak references,如果這個(gè)變量有可能是nil就用weak,如果永遠(yuǎn)不會(huì)為nil就用unowned references。

weak必須是var不能是let哦,畢竟是會(huì)被設(shè)為nil的,并且必須是optional的。例如weak var tenant: Person?

unowned reference和weak的區(qū)別是,總是會(huì)有值的,所以是永遠(yuǎn)不optional的~ 所以如果用unowned修飾變量,arc是不能把它設(shè)為nil的,所以如果它指向的對(duì)象dealloc了這個(gè)地方和unsafe_retain一樣不會(huì)置空。如果unowned的對(duì)象被釋放,然后你再通過unowned指針訪問它,就會(huì)出現(xiàn)crash

假設(shè)兩個(gè)類分別是CreditCard以及customer,顧客不一定會(huì)有信用卡,但是每個(gè)信用卡一定會(huì)有顧客,所以這里可以用unowned修飾信用卡的主人,因?yàn)橛肋h(yuǎn)不會(huì)是nil,并且還保證了沒有循環(huán)引用。例如unowned let customer: Customer。

在這種時(shí)候,如果置空了customer,由于沒有人strong持有customer,顧客就會(huì)被dealloc,然后也就沒有人strong持有credit card了,于是兩個(gè)就都被回收了~

let修飾的變量如果沒default就要在init里面初始化哦,然后如果是下面這種其實(shí)是會(huì)報(bào)錯(cuò)的,因?yàn)閕nit city的時(shí)候用到了沒有初始化完全的city,但這種感覺就很矛盾不太好改的樣子0.0 :

class Country {
    let name: String
    let capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    var name: String
    unowned let country: Country
    init (name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Implicitly Unwrapped Optionals:就等于說你每次對(duì)這種類型的值操作時(shí),都會(huì)自動(dòng)在操作前補(bǔ)上一個(gè)!進(jìn)行拆包,然后在執(zhí)行后面的操作,當(dāng)然如果該值是nil,也一樣會(huì)報(bào)錯(cuò)crash掉。

如果屬性不是在init而是在viewDidLoad這種里面賦值,但是又確定不會(huì)是nil,那么就可以用:(可參考:http://www.itdecent.cn/p/d58558944e84

var myLabel: UILabel!  //!相當(dāng)于下面這種寫法的語法糖
var myLabel: ImplicitlyUnwrappedOptional<</span>UILabel>

closure類似block,也會(huì)和對(duì)象建立循環(huán)引用的,包括property的也會(huì)哦:

class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
}
強(qiáng)引用環(huán)

The fact that asHTML is a lazy property means that you can refer to self within the default closure, because the lazy property will not be accessed until after initialization has been completed and self is known to exist.

而且在closure里面的時(shí)候,swift強(qiáng)制你使用self.來訪問屬性,這樣也可以提醒自己其實(shí)捕獲了self。

Capture List

Capture List是用來解決closure循環(huán)引用的問題噠~ 只要在closure聲明的前面增加一個(gè)[],然后包裹住捕獲的對(duì)象即可:

lazy var someClosure1: (Int, String) -> String = {
    [unowned self] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
    return ""
}

===如果closure的type可推測(cè)===
lazy var someClosure2: () -> String = {
    [unowned self] in
    // closure body goes here
    return ""
}

unowned來修飾closure里面的self,代表著兩者必須同時(shí)dealloc;如果對(duì)象會(huì)想dealloc,closure還持有著self那么就需要用weak啦,因?yàn)檫@個(gè)時(shí)候如果用unowned再訪問已經(jīng)被銷毀的對(duì)象會(huì)crash


Optional Chaining

?來gracefully解包,而!是強(qiáng)制解包如果是nil會(huì)crash的。用optional的變量來做事情即使本來func的return type是non-optional的,最后的結(jié)果也是optional的,因?yàn)椴淮_定是不是會(huì)被調(diào)到~ 但是注意不能用?來給optional的對(duì)象的屬性賦值哦,賦值只能john.residence!.address = johnsAddress

對(duì)optional的對(duì)象進(jìn)行方法調(diào)用也是類似的,即使方法沒有返回值,其實(shí)也是返回Void的:

if john.residence?.printNumberOfRooms() {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}

john.residence?.printNumberOfRooms()的類型其實(shí)是Void?,所以如果調(diào)用成功其實(shí)會(huì)走if的邏輯。如果沒有成功那么john.residence?.printNumberOfRooms()的值其實(shí)是nil。

訪問optional變量的下標(biāo)的時(shí)候也是需要在optional變量后面加問號(hào)的f let firstRoomName = john.residence?[0].name


Type Casting

is是用來對(duì)象判斷是不是某個(gè)Class的,如果是就會(huì)return true,例如if item is Movie。

as是用于向下轉(zhuǎn)型的~ as?返回的是optional的,因?yàn)椴灰欢梢猿晒D(zhuǎn)型,而as是強(qiáng)制向下轉(zhuǎn)型,如果沒有成功轉(zhuǎn)回crash的。

AnyAnyObject可以用于指代任何類型。AnyObject 可以用于轉(zhuǎn)OC里面的object類型,也對(duì)應(yīng)swift里面的對(duì)象類型,Any是包含了基本數(shù)據(jù)類型

如果是AnyObject的數(shù)組可以直接整個(gè)數(shù)組向下轉(zhuǎn)型的:for movie in someObjects as Movie[]。

例如case let someInt as Int:,在case的時(shí)候比較特殊,強(qiáng)制as轉(zhuǎn)型而非as?是沒有問題的,as在這里是check and cast to a specific type.


Extensions

和OC里面的category很類似,區(qū)別是Swift的extension沒有名字。類似的是,extension也是在define之前創(chuàng)建的對(duì)象也可以使用,和OC的category加載類似吧

功能:

  • Add computed properties and computed static properties
  • Define instance methods and type methods
  • Provide new initializers
  • Define subscripts
  • Define and use new nested types
  • Make an existing type conform to a protocol
extension SomeType {
  // new functionality to add to SomeType goes here
}

注意哦,extension可以加計(jì)算屬性,但是不能加存儲(chǔ)屬性以及屬性observer

extension可以增加convenience initializers,但是不能增加Designated initializersdeinit,這倆必須在原類里面寫。


Protocols

protocol SomeProtocol {
  // protocol definition goes here
}

如果是遵循多個(gè)protocol,或者有父類都可以列在一起:class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol

在protocol里面對(duì)屬性聲明不會(huì)指明是存儲(chǔ)屬性還是計(jì)算屬性,但是必須聲明type以及setter和getter。

例如:

protocol SomeProtocol {
  var mustBeSettable: Int { get set }
  var doesNotNeedToBeSettable: Int { get }
}

如果是set和get都需要的屬性,是不能用let的變量以及只有g(shù)et的計(jì)算屬性滿足的哦。

在協(xié)議里面如果定義的是類的屬性,而非對(duì)象的屬性,不是用static而是用class哦。類方法也是類似的,應(yīng)該是修飾class,而在實(shí)現(xiàn)的時(shí)候修飾static

protocol AnotherProtocol {
  class var someTypeProperty: Int { get set }
}

protocol SomeProtocol {
  class func someTypeMethod()
}

在protocol里面聲明方法的時(shí)候,可以聲明參數(shù)類型,但是不能聲明默認(rèn)值哦。在protocol里面如果用mutating標(biāo)記了方法,那么在實(shí)現(xiàn)的時(shí)候可以不標(biāo)記mutating~

聲明對(duì)象遵循某個(gè)protocol的時(shí)候其實(shí)協(xié)議就被當(dāng)做類型了,例如let generator: RandomNumberGenerator

類似category可以給現(xiàn)有的類加protocol,extension也可以拓展現(xiàn)有的類增加遵從的協(xié)議,尤其在你無法改這個(gè)類的source code的時(shí)候

protocol TextRepresentable {
    func asText() -> String
}
extension Dice: TextRepresentable {
    func asText() -> String {
        return "A \(sides)-sided dice"
    }
}

protocol的繼承和class是一致的,:protocol InheritingProtocol: SomeProtocol, AnotherProtocol,如果參數(shù)是遵循某個(gè)協(xié)議可以這樣來寫celebrator: protocol<Named, Aged>

關(guān)于POP的一些:https://www.cnblogs.com/guohai-stronger/p/12359303.html

isas可以看對(duì)象是不是遵循了某個(gè)protocol的~ 但是如果想這么用,這個(gè)protocol必須標(biāo)記為@objc,注意哦,@objc是不能給枚舉或者struct標(biāo)記的哦~

如果方法是optional的需要這樣標(biāo)記:(@objc是因?yàn)閛ptional的方法必須這樣標(biāo)記,所以protocol也需要標(biāo)記@objc)

@objc protocol Card {
    @objc optional func refresh()
}

調(diào)用protocol的optional方法的時(shí)候,不像OC那種需要先看是不是responseTo,只要用someOptionalMethod?(someArgument)這種?調(diào)用即可,返回值就也是optional的,例如if let amount = dataSource?.incrementForCount?(count)


Generics

array和dictionary其實(shí)都是泛型,因?yàn)榭梢匝bint也能裝string。

假設(shè)有這樣一個(gè)函數(shù)來交換整數(shù):

func swapTwoInts(inout a: Int, inout b: Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

那如果我們想交換浮點(diǎn)數(shù)呢?再寫一個(gè) swapTwoFloats ?但這里又不能寫一個(gè)通用方法交換兩個(gè)類型Any的對(duì)象,因?yàn)槿绻梢越粨Q類型必須一致的。

可以借助泛型改造這樣:

func swapTwoValues<T>( a: inout T, b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

<T>The brackets tell Swift that T is a placeholder type name within the swapTwoValues function definition. Because T is a placeholder, Swift does not look for an actual type called T。這樣參數(shù)和return type就都可以用T了。

泛型的名字可以起的更清晰一點(diǎn),例如對(duì)dictionary可以取名為KeyTypeValueType

對(duì)于class例如array要這么定義:

struct Stack<T> {
    var items = T[]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
}

創(chuàng)建的時(shí)候醬紫:var stackOfStrings = Stack<String>()

如果你要給泛型加限制,比如dictionary的KeyType應(yīng)該是Hashable的,就可以醬紫func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U),另外例如如果你想比較兩個(gè)泛型對(duì)象,這倆對(duì)象的泛型就應(yīng)該滿足Equatable。

如果在protocol里面用泛型是醬紫的:

protocol Container {
    associatedtype ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

使用這個(gè)protocol的時(shí)候再impl里面typealias ItemType = Int這樣來標(biāo)注ItemType是int類型的~ 如果你不寫也可以,因?yàn)閟wift可以自己自動(dòng)infer出來~

where也可以用于約束泛型例如醬紫:func allItemsMatch<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>。


Advanced Operators

swift里面的overflow默認(rèn)是會(huì)運(yùn)行時(shí)error的,如果你想兼容overflow那么需要用&+來替代+,所有overflow的操作符都以&開頭。

例如:

var potentialOverflow = Int16.max
// potentialOverflow equals 32767, which is the largest value an Int16 can hold 
potentialOverflow += 1
// this causes an error

var willOverflow = UInt8.max
// willOverflow equals 255, which is the largest value a UInt8 can hold 
willOverflow = willOverflow &+ 1
// willOverflow is now equal to 0

并且&/&%return a value of zero if you divide by zero~

~取反:0b00001111 -> 11110000
&位與:0b11111100 & 0b00111111 -> 00111100
|位或:0b10110010 | 0b01011110 -> 11111110
^異或: 0b00010100 ^ 0b00000101 -> 00010001
<<左移,<<右移,如果是signed value會(huì)保持sign位正負(fù)號(hào)不變

運(yùn)算符也是可以重載的~例如:(你還可以定義-或者++、==、!=神馬的)

func + (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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