[TOC]html
@(swift)[溫故而知新]node
面試中常常被問到
Objective-C
與Swift
的區別,其實區別仍是不少的,重點整理一下我的以爲很重要的:面向協議編程。git
OC
出現過的絕大多數概念,好比引用計數、ARC(自動引用計數)、屬性、協議、接口、初始化、擴展類、命名參數、匿名函數等,在Swift
中繼續有效(可能最多換個術語)。Swift
和Objective-C
共用一套運行時環境,Swift
的類型能夠橋接到Objective-C
(下面我簡稱OC),反之亦然OC
注重靈活OC
注重面向對象編程OC
注重指針和引用OC
是動態類型語言OC
中的nil
更加安全和簡明OC
中只能爲集合類型添加泛型函數式編程
) (Swift的標準數組支持三個高階函數:map
,filter
和reduce
,以及map的擴展flatMap
)open
> public
> internal(默認)
> fileprivate
> private
tuples
),把多個值組合成複合值。元組內的值能夠是任何類型,並不要求是相同類型的。Swift 跟 JavaScript 有什麼相同和不一樣點?github
從數據結構角度,Golang和Swift對比,有何優缺點?面試
區別 | class |
struct |
---|---|---|
定義屬性用於存儲值 | 是 | 是 |
定義方法用於提供功能 | 是 | 是 |
定義附屬腳本用於訪問值 | 是 | 是 |
定義構造器用於生成初始化值 | 是 | 是 |
經過擴展以增長默認實現的功能 | 是 | 是 |
遵照協議以對某類提供標準功能 | 是 | 是 |
是否能夠繼承 | 是 | 否 |
是否能夠引用計數 | 是 | 否 |
類型轉換 | 是 | 否 |
析構方法釋放資源 | 是 | 否 |
class Class {
// class definition goes here
}
struct Structure {
// structure definition goes here
}
複製代碼
一、結構體 struct
和枚舉 enum
是值類型,類 class
是引用類型。 二、String
, Array
和 Dictionary
都是結構體,所以賦值直接是拷貝,而NSString
, NSArray
和NSDictionary
則是類,因此是使用引用的方式。 三、struct
比 class
更「輕量級」,struct
分配在棧中,class
分配在堆中。swift
若是你有 C,C++ 或者 Objective-C 語言的經驗,那麼你也許會知道這些語言使用指針來引用內存中的地址。一個 Swift 常量或者變量引用一個引用類型的實例與 C 語言中的指針相似,不一樣的是並不直接指向內存中的某個地址,並且也不要求你使用星號(*)來代表你在建立一個引用。Swift 中這些引用與其它的常量或變量的定義方式相同。數組
使用
struct
:任何狀況下,優先考慮使用struct,若是知足不了,再考慮class
安全
使用
class
bash
node
選用class
而不是struct
)相比於OC
,Swift
能夠作到protocol
協議方法的具體默認實現(經過extension
)相比多態
更好的實現了代碼複用,而 OC
則不行。
面向協議
(面向接口
)與面向對象
的區別
面向對象
和面向協議
的的最明顯區別是對抽象數據的使用方式
,面向對象採用的是繼承,而面向協議
採用的是遵照協議。在面向協議
設計中,Apple
建議咱們更多的使用 值類型 (struct
)而非 引用類型 (class
)。這篇文章中有一個很好的例子說明了面向協議
比面向對象
更符合某些業務需求。其中有飛機、汽車、自行車三種交通工具(均繼承自父類交通工具);老虎、馬三種動物(均繼承父類自動物);在古代馬其實也是一種交通工具,可是父類是動物,若是馬也有交通工具的功能,則:
若是採用
面向對象編程
,則須要既要繼承動物,還要繼承交通工具,可是父類交通工具備些功能馬是不須要的。因而可知繼承,做爲代碼複用的一種方式,耦合性仍是太強。事物每每是一系列特質的組合,而不僅僅是以一脈相承並逐漸擴展的方式構建的。之後慢慢會發現面向對象不少時候其實不能很好地對事物進行抽象。
若是採用
面向協議編程
,馬只須要實現出行協議就能夠擁有交通工具的功能了。面向協議
就是這樣的抽離方式,更好的職責劃分,更加具象化,職責更加單一。很明顯面向協議
的目的是爲了下降代碼的耦合性。
總結:
面向協議
相對於面向對象
來講更具備可伸縮性和可重用性,而且在編程的過程當中更加模塊化,經過協議以及協議擴展替代一個龐大的基類,這在大規模系統編程中會有很大的便捷之處。
協議
和協議擴展
比基類
有三個明顯的優勢:一、類型能夠遵照多個協議可是隻有一個基類。
這意味着類型能夠隨意遵照任何想要特性的協議,而不須要一個巨大的基類。二、不須要知道源碼就可使用協議擴展添加功能。
這意味着咱們能夠任意擴展協議,包括swift內置的協議,而不須要修改基類的源碼。通常狀況下咱們會給特定的類而非類的層級(繼承體系)添加擴展;可是若是必要,咱們依然能夠給基類添加擴展,使全部的子類繼承添加的功能。使用協議,全部的屬性、方法和構造函數都被定義在遵照協議的類型自身中。這讓咱們很容易地查看到全部東西是怎麼被定義和初始化的。咱們不須要在類的層級之間來回穿梭以查看全部東西是如何初始化的。忘記設置超類可能沒有什麼大問題,可是在更復雜的類型中,忘記合理地設置某個屬性可能會致使意想不到的行爲。三、協議能夠被類、結構體和枚舉遵照,而類層級約束爲類類型。
協議和協議擴展可讓咱們在更合理的地方使用值類型。引用類型和值類型的一個主要的區別就是類型是如何傳遞的。當咱們傳遞引用類型(class)的實例時,咱們傳遞的對原實例的引用。這意味着所作的任何更改都會反射回原實例中。當咱們傳遞值類型的實例時,咱們傳遞的是對原實例的一份拷貝。這意味着所作的任何更改都不會反射回原實例中。使用值類型確保了咱們老是獲得一個惟一的實例由於咱們傳遞了一份對原實例的拷貝而非對原實例的引用。所以,咱們能相信沒有代碼中的其它部分會意外地修改咱們的實例。這在多線程環境中尤爲有用,其中不一樣的線程能夠修改數據並建立意外地行爲。數據封裝、訪問控制、隱藏實現細節、類型抽象爲類;
代碼以邏輯關係組織到一塊兒,方便閱讀;
高內聚、低耦合的系統結構
代碼重用,繼承關係,更符合人類思惟
接口重用,父類指針可以指向子類對象
當父類的引用指向子類對象時,就發生了向上轉型,即把子類類型對象轉成了父類類型。
向上轉型的好處是隱藏了子類類型,提升了代碼的擴展性。
面向對象
的三個困境:在
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 <NSObject>
- (void)func;
@end
@interface SafeObj : NSObject<SafeProtocol>
@end
@implementation SafeObj
- (void)func {
}
@end
@interface UnSafeObj : NSObject
@end
@implementation UnSafeObj
@end
複製代碼
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<SafeProtocol>'
NSArray<id<SafeProtocol>> *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")
複製代碼
咱們很難在不一樣的繼承體系中複用代碼,用行話來說就是橫切關注點(Cross-Cutting Concerns
)。好比下面的關注點myMethod
,位於兩條繼承鏈 (UIViewController
-> ViewCotroller
和 UIViewController
-> UITableViewController
-> AnotherViewController
) 的橫切面上。面向對象是一種不錯的抽象方式,可是確定不是最好的方式。它沒法描述兩個不一樣事物具備某個相同特性這一點。在這裏,特性的組合要比繼承更貼切事物的本質。
class ViewCotroller: UIViewController {
func myMethod() {
}
}
複製代碼
class AnotherViewController: UITableViewController {
func myMethod() {
}
}
複製代碼
在面向對象編程
中,針對這種問題的幾種解決方案:
快速,可是這也是壞代碼的開頭。咱們應該儘可能避免這種作法。
看起來這是一個稍微靠譜的作法,可是若是不斷這麼作,會讓所謂的
Base
很快變成垃圾堆。職責不明確,任何東西都能扔進Base
,你徹底不知道哪些類走了Base
,而這個「超級類」對代碼的影響也會不可預估。
經過外界傳入一個帶有 myMethod 的對象,用新的類型來提供這個功能。這是一個稍好的方式,可是引入額外的依賴關係,可能也是咱們不太願意看到的。
固然,
Swift
是不支持多繼承的。不過若是有多繼承的話,咱們確實能夠從多個父類進行繼承,並將myMethod
添加到合適的地方。有一些語言選擇了支持多繼承 (好比C++
),可是它會帶來OOP
中另外一個著名的問題:菱形缺陷。
在
Swift
的面向協議編程
中,針對這種問題的解決方案(使用協議擴展添加默認實現):
protocol P {
func myMethod()
}
extension P {
func myMethod() {
doWork()
}
}
複製代碼
extension ViewController: P { }
extension AnotherViewController: P { }
viewController.myMethod()
anotherViewController.myMethod()
複製代碼
多繼承中,兩個父類實現了相同的方法,子類沒法肯定繼承哪一個父類的此方法,因爲多繼承的拓撲結構是一個菱形,因此這個問題有被叫作菱形缺陷(
Diamond Problem
)。
上面的例子中,若是咱們有多繼承,那麼 ViewController
和 AnotherViewController
的關係可能會是這樣的:
若是ViewController
和UITableViewController
都實現了myMethod
方法,則在AnotherViewController
中就會出現菱形缺陷問題。
咱們應該着眼於寫乾淨並安全的代碼,乾淨的代碼是很是易讀和易理解的代碼。
protocol
的鏈表實現import UIKit
protocol ChainListAble {
associatedtype T: Equatable
// 打印
var description: String{get}
// 數量
var count: Int{get}
/// 插入
func insertToHead(node: Node<T>)
func insertToHead(value: T)
func insertToTail(node: Node<T>)
func insertToTail(value: T)
func insert(node: Node<T>, afterNode: Node<T>) -> Bool
func insert(value: T, afterNode: Node<T>) -> Bool
func insert(node: Node<T>, beforNode: Node<T>) -> Bool
func insert(value: T, beforNode: Node<T>) -> Bool
/// 刪除(默認第一個符合條件的)
@discardableResult func delete(node: Node<T>) -> 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<T>?
func find(index: Int) -> Node<T>?
/// 判斷結點是否在鏈表中
func isContain(node: Node<T>) -> Bool
}
/// [值類型不能在遞歸裏調用](https://www.codetd.com/article/40263),所以Node類型只能是class而不是struct
// 有些時候你只能使用類而不能使用結構體,那就是遞歸裏
// struct報錯:Value type 'Node' cannot have a stored property that recursively contains it
class Node<T: Equatable> {
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<T>) {
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<T>) {
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<T>) -> 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<T>, afterNode: Node<T>) -> 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<T>) -> 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<T>, beforNode: Node<T>) -> 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<T>) -> 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<T>? {
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<T>? {
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<T>) -> 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<String>? = self.dummy.next
var curNode: Node<String>? = prevNode?.next
var tempNode: Node<String>? = 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<T>?, 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)
}
}
複製代碼
一、面向協議編程與 Cocoa 的邂逅 (上) 二、面向協議編程與 Cocoa 的邂逅 (下) 三、[譯] Swift 面向協議編程入門