天天看點

swift string轉int_來一次有側重點的區分Swift與Objective-C一、Objective-C與Swift的異同二、swift類(class)和結構體(struct)的差別三、Objective-C中的protocol與Swift中的protocol的差別四、面向協定(面向接口)與面向對象的差別程式設計實踐:基于protocol的連結清單實作

swift string轉int_來一次有側重點的區分Swift與Objective-C一、Objective-C與Swift的異同二、swift類(class)和結構體(struct)的差別三、Objective-C中的protocol與Swift中的protocol的差別四、面向協定(面向接口)與面向對象的差別程式設計實踐:基于protocol的連結清單實作
面試中經常被問到Objective-C與Swift的差別,其實差別還是很多的,重點整理一下個人覺得很重要的:面向協定程式設計。

一、Objective-C與Swift的異同

1.1、swift和OC的共同點:

  • - OC出現過的絕大多數概念,比如引用計數、ARC(自動引用計數)、屬性、協定、接口、初始化、擴充類、命名參數、匿名函數等,在Swift中繼續有效(可能最多換個術語)。
  • - Swift和Objective-C共用一套運作時環境,Swift的類型可以橋接到Objective-C(下面我簡稱OC),反之亦然

1.2、swift的優點:

  • - swift注重安全,OC注重靈活
  • - swift注重面向協定程式設計、函數式程式設計、面向對象程式設計,OC注重面向對象程式設計
  • - swift注重值類型,OC注重指針和引用
  • - swift是靜态類型語言,OC是動态類型語言
  • - swift容易閱讀,檔案結構和大部分文法簡易化,隻有.swift檔案,結尾不需要分号
  • - swift中的可選類型,是用于所有資料類型,而不僅僅局限于類。相比于OC中的nil更加安全和簡明
  • - swift中的泛型類型更加友善和通用,而非OC中隻能為集合類型添加泛型
  • - swift中各種友善快捷的高階函數(函數式程式設計) (Swift的标準數組支援三個高階函數:map,filter和reduce,以及map的擴充flatMap)
  • - swift新增了兩種權限,細化權限。open > public > internal(預設) > fileprivate > private
  • - swift中獨有的元組類型(tuples),把多個值組合成複合值。元組内的值可以是任何類型,并不要求是相同類型的。

1.3、swift的不足:

  • - 版本不穩定
  • - 公司使用比例不高,使用人數比例偏低
  • - 有些文法其實可以隻規定一種格式,不必這樣也行,那樣也行。像Go一樣禁止一切(Go有點偏激)耍花槍的東西,同一個規範,友善團隊合作和閱讀他人代碼。

Swift 跟 JavaScript 有什麼相同和不同點?

從資料結構角度,Golang和Swift對比,有何優缺點?

iOS——Objective-C與Swift優缺點對比

二、swift類(class)和結構體(struct)的差別

差別 class struct 定義屬性用于存儲值 是 是 定義方法用于提供功能 是 是 定義附屬腳本用于通路值 是 是 定義構造器用于生成初始化值 是 是 通過擴充以增加預設實作的功能 是 是 遵守協定以對某類提供标準功能 是 是 是否可以繼承 是 否 是否可以引用計數 是 否 類型轉換 是 否 析構方法釋放資源 是 否 2.1 定義

class Class { // class definition goes here}struct Structure { // structure definition goes here}複制代碼
           

2.2 值 VS 引用

1、結構體 struct 和枚舉 enum 是值類型,類 class 是引用類型。 2、String, Array和 Dictionary都是結構體,是以指派直接是拷貝,而NSString, NSArray 和NSDictionary則是類,是以是使用引用的方式。 3、struct 比 class 更“輕量級”,struct 配置設定在棧中,class 配置設定在堆中。

2.3 指針

如果你有 C,C++ 或者 Objective-C 語言的經驗,那麼你也許會知道這些語言使用指針來引用記憶體中的位址。一個 Swift 常量或者變量引用一個引用類型的執行個體與 C 語言中的指針類似,不同的是并不直接指向記憶體中的某個位址,而且也不要求你使用星号(*)來表明你在建立一個引用。Swift 中這些引用與其它的常量或變量的定義方式相同。

2.4 選擇使用類和結構體

使用struct:任何情況下,優先考慮使用struct,如果滿足不了,再考慮class
  • - 比如資料被多線程使用,而且資料沒有使用class的必要性,就使用struct
  • - 希望執行個體被拷貝時,不收拷貝後的新執行個體的影響
  • - 幾何圖形的大小,可以封裝width和height屬性,都是Double類型
  • - 指向連續序列範圍的方法,可以封裝start和length屬性,都是Int類型
  • - 一個在3D坐标系統的點, 可以封裝x, y和z屬性,都是Double類型
使用class
  • - 需要繼承
  • - 被遞歸調用的時候(參考連結清單的實作,node選用class而不是struct)
  • - 屬性資料複雜
  • - 希望引用而不是拷貝

三、Objective-C中的protocol與Swift中的protocol的差別

相比于OC,Swift 可以做到protocol協定方法的具體預設實作(通過extension)相比多态更好的實作了代碼複用,而 OC 則不行。

四、面向協定(面向接口)與面向對象的差別

面向對象和面向協定的的最明顯差別是對抽象資料的使用方式,面向對象采用的是繼承,而面向協定采用的是遵守協定。在面向協定設計中,Apple建議我們更多的使用 值類型 (struct)而非 引用類型 (class)。這篇文章中有一個很好的例子說明了面向協定比面向對象更符合某些業務需求。其中有飛機、汽車、自行車三種交通工具(均繼承自父類交通工具);老虎、馬三種動物(均繼承父類自動物);在古代馬其實也是一種交通工具,但是父類是動物,如果馬也有交通工具的功能,則:
如果采用面向對象程式設計,則需要既要繼承動物,還要繼承交通工具,但是父類交通工具有些功能馬是不需要的。由此可見繼承,作為代碼複用的一種方式,耦合性還是太強。事物往往是一系列特質的組合,而不單單是以一脈相承并逐漸擴充的方式建構的。以後慢慢會發現面向對象很多時候其實不能很好地對事物進行抽象。
swift string轉int_來一次有側重點的區分Swift與Objective-C一、Objective-C與Swift的異同二、swift類(class)和結構體(struct)的差別三、Objective-C中的protocol與Swift中的protocol的差別四、面向協定(面向接口)與面向對象的差別程式設計實踐:基于protocol的連結清單實作
如果采用面向協定程式設計,馬隻需要實作出行協定就可以擁有交通工具的功能了。面向協定就是這樣的抽離方式,更好的職責劃分,更加具象化,職責更加單一。很明顯面向協定的目的是為了降低代碼的耦合性。
swift string轉int_來一次有側重點的區分Swift與Objective-C一、Objective-C與Swift的異同二、swift類(class)和結構體(struct)的差別三、Objective-C中的protocol與Swift中的protocol的差別四、面向協定(面向接口)與面向對象的差別程式設計實踐:基于protocol的連結清單實作

總結:

面向協定相對于面向對象來說更具有可伸縮性和可重用性,并且在程式設計的過程中更加子產品化,通過協定以及協定擴充替代一個龐大的基類,這在大規模系統程式設計中會有很大的便捷之處。

3.1、協定和協定擴充比基類有三個明顯的優點:

  • 1、類型可以遵守多個協定但是隻有一個基類。 這意味着類型可以随意遵守任何想要特性的協定,而不需要一個巨大的基類。
  • 2、不需要知道源碼就可以使用協定擴充添加功能。這意味着我們可以任意擴充協定,包括swift内置的協定,而不需要修改基類的源碼。一般情況下我們會給特定的類而非類的層級(繼承體系)添加擴充;但是如果必要,我們依然可以給基類添加擴充,使所有的子類繼承添加的功能。使用協定,所有的屬性、方法和構造函數都被定義在遵守協定的類型自身中。這讓我們很容易地檢視到所有東西是怎麼被定義和初始化的。我們不需要在類的層級之間來回穿梭以檢視所有東西是如何初始化的。忘記設定超類可能沒有什麼大問題,但是在更複雜的類型中,忘記合理地設定某個屬性可能會導緻意想不到的行為。
  • 3、協定可以被類、結構體和枚舉遵守,而類層級限制為類類型。 協定和協定擴充可以讓我們在更合理的地方使用值類型。引用類型和值類型的一個主要的差別就是類型是如何傳遞的。當我們傳遞引用類型(class)的執行個體時,我們傳遞的對原執行個體的引用。這意味着所做的任何更改都會反射回原執行個體中。當我們傳遞值類型的執行個體時,我們傳遞的是對原執行個體的一份拷貝。這意味着所做的任何更改都不會反射回原執行個體中。使用值類型確定了我們總是得到一個唯一的執行個體因為我們傳遞了一份對原執行個體的拷貝而非對原執行個體的引用。是以,我們能相信沒有代碼中的其它部分會意外地修改我們的執行個體。這在多線程環境中尤其有用,其中不同的線程可以修改資料并建立意外地行為。

3.2、面向對象的特點

優點:

  • - 封裝

資料封裝、通路控制、隐藏實作細節、類型抽象為類;

代碼以邏輯關系組織到一起,友善閱讀;

高内聚、低耦合的系統結構

  • - 繼承
代碼重用,繼承關系,更符合人類思維
  • - 多态

接口重用,父類指針能夠指向子類對象

當父類的引用指向子類對象時,就發生了向上轉型,即把子類類型對象轉成了父類類型。

向上轉型的好處是隐藏了子類類型,提高了代碼的擴充性。

多态的不足:

  • - 父類有部分public方法是子類不需要的,也不允許子類覆寫重寫
  • - 父類有一些方法是必須要子類去覆寫重寫的,在父類的方法其實也是一個空方法
  • - 父類的一些方法即便被子類覆寫重寫,父類原方法還是要執行的
  • - 父類的一些方法是可選覆寫重寫的,一旦被覆寫重寫,則以子類為準

較好的抽象類型應該:

  • - 更多地支援值類型,同時也支援引用類型
  • - 更多地支援靜态類型關聯(編譯期),同時也支援動态派發(runtime)
  • - 結構不龐大不複雜
  • - 模型可擴充
  • - 不給模型強制添加資料
  • - 不給模型增加初始化任務的負擔
  • - 清楚哪些方法該實作哪些方法不需實作

3.3、OneV's Den提到的面向對象的三個困境:

1、動态派發的安全性(這應該是OC的困境,在Swift中Xcode是不可能讓這種問題編譯通過的)

在Objective-C中下面這段代碼編譯是不會報警告和錯誤的
NSObject *v1 = [NSObject new];NSString *v2 = [NSString new];NSNumber *v3 = [NSNumber new];NSArray *array = @[v1, v2, v3];for (id obj in array) { [obj boolValue];}複制代碼
           
在Objective-C中可以借助泛型檢查這種潛在的問題,Xocde會提示警告
@protocol SafeProtocol - (void)func;@[email protected] SafeObj : [email protected]@implementation SafeObj- (void)func { }@[email protected] UnSafeObj : [email protected]@implementation [email protected]複制代碼
           
Objective-C在Xcode7中,可以使用帶泛型的容器也可以解決這個問題,但是隻是⚠️,程式運作期間仍可能由于此問題導緻的崩潰
SafeObj *v1 = [[SafeObj alloc] init];UnSafeObj *v2 = [[UnSafeObj alloc] init];// 由于v2沒有實作協定SafeProtocol,是以此處Xcode會有警告// Object of type 'UnSafeObj *' is not compatible with array element type 'id'NSArray> *array = @[v1, v2];for (id obj in array) { [obj func];}複制代碼
           
使用swift,必須指定類型,否則不是⚠️,而是❌,是以swift在編譯階段就可以檢查出問題
// 直接報錯,而不是警告// Cannot convert value of type 'String' to expected argument type 'NSNumber'var array: [NSNumber] = []array.append(1)array.append("a")複制代碼
           

2、橫切關注點

我們很難在不同的繼承體系中複用代碼,用行話來講就是橫切關注點(Cross-Cutting Concerns)。比如下面的關注點myMethod,位于兩條繼承鍊 (UIViewController -> ViewCotroller 和 UIViewController -> UITableViewController -> AnotherViewController) 的橫切面上。面向對象是一種不錯的抽象方式,但是肯定不是最好的方式。它無法描述兩個不同僚物具有某個相同特性這一點。在這裡,特性的組合要比繼承更貼切事物的本質。

class ViewCotroller: UIViewController {  func myMethod() {  }}複制代碼class AnotherViewController: UITableViewController { func myMethod() {  }}複制代碼
           

在面向對象程式設計中,針對這種問題的幾種解決方案:

  • - 1、Copy & Paste
快速,但是這也是壞代碼的開頭。我們應該盡量避免這種做法。
  • - 2、引入 BaseViewController
看起來這是一個稍微靠譜的做法,但是如果不斷這麼做,會讓所謂的 Base 很快變成垃圾堆。職責不明确,任何東西都能扔進 Base,你完全不知道哪些類走了 Base,而這個“超級類”對代碼的影響也會不可預估。
  • - 3、依賴注入
通過外界傳入一個帶有 myMethod 的對象,用新的類型來提供這個功能。這是一個稍好的方式,但是引入額外的依賴關系,可能也是我們不太願意看到的。
  • - 4、多繼承

當然,Swift 是不支援多繼承的。不過如果有多繼承的話,我們确實可以從多個父類進行繼承,并将 myMethod 添加到合适的地方。有一些語言選擇了支援多繼承 (比如 C++),但是它會帶來 OOP 中另一個著名的問題:菱形缺陷。

在Swift的面向協定程式設計中,針對這種問題的解決方案(使用協定擴充添加預設實作):

protocol P { func myMethod()}extension P { func myMethod() { doWork() }}複制代碼extension ViewController: P { }extension AnotherViewController: P { }viewController.myMethod()anotherViewController.myMethod()複制代碼
           

3、菱形問題

多繼承中,兩個父類實作了相同的方法,子類無法确定繼承哪個父類的此方法,由于多繼承的拓撲結構是一個菱形,是以這個問題有被叫做菱形缺陷(Diamond Problem)。

上面的例子中,如果我們有多繼承,那麼 ViewController 和 AnotherViewController 的關系可能會是這樣的:

swift string轉int_來一次有側重點的區分Swift與Objective-C一、Objective-C與Swift的異同二、swift類(class)和結構體(struct)的差別三、Objective-C中的protocol與Swift中的protocol的差別四、面向協定(面向接口)與面向對象的差別程式設計實踐:基于protocol的連結清單實作

如果ViewController 和UITableViewController都實作了myMethod方法,則在AnotherViewController中就會出現菱形缺陷問題。

我們應該着眼于寫幹淨并安全的代碼,幹淨的代碼是非常易讀和易了解的代碼。

程式設計實踐:基于protocol的連結清單實作

import UIKitprotocol ChainListAble { associatedtype T: Equatable // 列印 var description: String{get} // 數量 var count: Int{get}  /// 插入 func insertToHead(node: Node) func insertToHead(value: T) func insertToTail(node: Node) func insertToTail(value: T) func insert(node: Node, afterNode: Node) -> Bool func insert(value: T, afterNode: Node) -> Bool func insert(node: Node, beforNode: Node) -> Bool func insert(value: T, beforNode: Node) -> Bool  /// 删除(預設第一個符合條件的) @discardableResult func delete(node: Node) -> Bool @discardableResult func delete(value: T) -> Bool @discardableResult func delete(index: Int) -> Bool //func delete(fromIndex: Int, toIndex: Int) -> Bool //func deleteAll()  /// 查找(預設第一個符合條件的) func find(value: T) -> Node? func find(index: Int) -> Node?  /// 判斷結點是否在連結清單中 func isContain(node: Node) -> Bool}/// [值類型不能在遞歸裡調用](https://www.codetd.com/article/40263),是以Node類型隻能是class而不是struct// 有些時候你隻能使用類而不能使用結構體,那就是遞歸裡// struct報錯:Value type 'Node' cannot have a stored property that recursively contains itclass Node { var value: T var next: Node?  /// 便利構造方法 /// /// - Parameter value: value convenience init(value: T) { self.init(value: value, next: nil) }  /// 預設指定初始化方法 /// /// - Parameters: /// - value: value /// - next: next init(value: T, next: Node?) { self.value = value }  // 銷毀函數 deinit { print("(self.value) 釋放") }}extension Node { /// 傳回目前結點到連結清單尾的長度 var count: Int { var idx: Int = 1 var node: Node? = self while node?.value != nil { node = node?.next idx = idx + 1 } return idx }}class SingleChainList: ChainListAble { typealias T = String // 哨兵結點,不存儲資料 private var dummy: Node = Node.init(value: "")}extension SingleChainList { var description: String { var description: String = "" var tempNode = self.dummy while let nextNode = tempNode.next { description = description + " " + nextNode.value tempNode = nextNode } return description } var count: Int { var count: Int = 0 var tempNode = self.dummy while let nextNode = tempNode.next { count = count + 1 tempNode = nextNode } return count }  /// 在頭部插入值 /// /// - Parameter value: value func insertToHead(value: T) { let node: Node = Node.init(value: value) self.insertToHead(node: node) } /// 在頭部插入結點 /// /// - Parameter node: node func insertToHead(node: Node) { node.next = self.dummy.next self.dummy.next = node } /// 在尾部插入值 /// /// - Parameter value: value func insertToTail(value: T) { let node: Node = Node.init(value: value) self.insertToTail(node: node) } /// 在尾部插入結點 /// /// - Parameter node: node func insertToTail(node: Node) { var tailNode: Node = self.dummy while let nextNode = tailNode.next { tailNode = nextNode } tailNode.next = node } /// 在指定結點的後面插入新value /// /// - Parameters: /// - value: 新值 /// - afterNode: 指定結點 /// - Returns: true or false func insert(value: T, afterNode: Node) -> Bool { let node: Node = Node.init(value: value) return self.insert(node: node, afterNode: afterNode) } /// 在指定結點的後面插入新結點 /// /// - Parameters: /// - value: 新結點 /// - afterNode: 指定結點 /// - Returns: true or false func insert(node: Node, afterNode: Node) -> Bool { guard self.isContain(node: afterNode) else { return false } node.next = afterNode.next afterNode.next = node return true } /// 在指定結點的前面插入新value(雙向連結清單實作這種插入方式速度比單向連結清單快) /// /// - Parameters: /// - value: 新值 /// - beforNode: 指定結點 /// - Returns: true or false func insert(value: T, beforNode: Node) -> Bool { let node: Node = Node.init(value: value) return self.insert(node: node, beforNode: beforNode) } /// 在指定結點的前面插入新結點(雙向連結清單實作這種插入方式速度比單向連結清單快) /// /// - Parameters: /// - node: 新結點 /// - beforNode: 指定結點 /// - Returns: true or false func insert(node: Node, beforNode: Node) -> Bool { var tempNode: Node = self.dummy while let nextNode = tempNode.next { if nextNode === beforNode { node.next = beforNode tempNode.next = node return true } tempNode = nextNode } return false } /// 删除指定value的結點 /// /// - Parameter value: value /// - Returns: true or false func delete(value: T) -> Bool { var tempNode: Node = self.dummy while let nextNode = tempNode.next { // 此處判斷 == 是否合理 if nextNode.value == value { tempNode.next = nextNode.next return true } tempNode = nextNode } return false } /// 删除指定的結點 /// /// - Parameter node: node /// - Returns: true or false func delete(node: Node) -> Bool { var tempNode = self.dummy while let nextNode = tempNode.next { if nextNode === node { tempNode.next = nextNode.next return true } tempNode = nextNode } return false } /// 删除指定下标的結點 /// /// - Parameter index: index /// - Returns: true or false func delete(index: Int) -> Bool { var idx: Int = 0 var tempNode: Node = self.dummy while let nextNode = tempNode.next { if index == idx { tempNode.next = nextNode.next return true } tempNode = nextNode idx = idx + 1 } return false }  /// 查找指定值的node /// /// - Parameter value: value /// - Returns: node func find(value: T) -> Node? { var tempNode = self.dummy while let nextNode = tempNode.next { if nextNode.value == value { return nextNode } tempNode = nextNode } return nil } /// 查找指定下标的結點 /// /// - Parameter index: index /// - Returns: node func find(index: Int) -> Node? { var idx: Int = 0 var tempNode: Node = self.dummy while let nextNode = tempNode.next { if index == idx { return nextNode } tempNode = nextNode idx = idx + 1 } return nil } /// 判斷給定的連結清單是否在連結清單中 /// /// - Parameter node: node /// - Returns: true or false func isContain(node: Node) -> Bool { var tempNode = self.dummy.next while tempNode != nil { if tempNode === node { return true } tempNode = tempNode?.next } return false } /// 單向連結清單反轉:方式一非遞歸實作 /// /// - Parameter chainList: 源連結清單 /// - Returns: 反轉後的連結清單 func reverseList() { var prevNode: Node? = self.dummy.next var curNode: Node? = prevNode?.next var tempNode: Node? = curNode?.next prevNode?.next = nil while curNode != nil {  tempNode = curNode?.next curNode?.next = prevNode prevNode = curNode curNode = tempNode } self.dummy.next = prevNode } /// 單向連結清單反轉:方式二遞歸實作 /// /// - Parameter chainList: 源連結清單 /// - Returns: 反轉後的連結清單 func reverseListUseRecursion(head: Node?, isFirst: Bool) { var tHead = head if isFirst { tHead = self.dummy.next } guard let rHead = tHead else { return } if rHead.next == nil { self.dummy.next = rHead return } else { self.reverseListUseRecursion(head:rHead.next, isFirst: false) rHead.next?.next = rHead rHead.next = nil } }}class LinkedListVC: UIViewController { var chainList: SingleChainList = SingleChainList.init() override func viewDidLoad() { super.viewDidLoad() // 初始化連結清單 for i in 0..<10 { let node: Node = Node.init(value: String(i)) chainList.insertToTail(node: node) } // 查找結點 for i in 0..<12 { if let find: Node = chainList.find(index: i) { debugPrint("find = (find.value)") } else { debugPrint("not find idx = (i)") } } // 删除結點 if chainList.delete(index: 10) { debugPrint("删除 index = (index)成功") } else { debugPrint("删除 index = (index)失敗") } // 列印結點value資訊 debugPrint(chainList.description) // 列印結點個數 debugPrint(chainList.count) // 單向連結清單反轉 chainList.reverseList() // 列印結點value資訊 debugPrint(chainList.description) // 單向連結清單反轉 chainList.reverseListUseRecursion(head: nil, isFirst: true) // 列印結點value資訊 debugPrint(chainList.description) }}