來一次有側重點的區分Swift與Objective-C

[TOC]html

@(swift)[溫故而知新]node

面試中常常被問到Objective-CSwift的區別,其實區別仍是不少的,重點整理一下我的以爲很重要的:面向協議編程git

1、Objective-C與Swift的異同

1.一、swift和OC的共同點:

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

1.二、swift的優勢:

  • - swift注重安全,OC注重靈活
  • - swift注重面向協議編程、函數式編程、面向對象編程,OC注重面向對象編程
  • - swift注重值類型,OC注重指針和引用
  • - swift是靜態類型語言,OC是動態類型語言
  • - swift容易閱讀,文件結構和大部分語法簡易化,只有.swift文件,結尾不須要分號
  • - swift中的可選類型,是用於全部數據類型,而不只僅侷限於類。相比於OC中的nil更加安全和簡明
  • - swift中的泛型類型更加方便和通用,而非OC中只能爲集合類型添加泛型
  • - swift中各類方便快捷的高階函數函數式編程) (Swift的標準數組支持三個高階函數:mapfilterreduce,以及map的擴展flatMap)
  • - swift新增了兩種權限,細化權限。open > public > internal(默認) > fileprivate > private
  • - swift中獨有的元組類型(tuples),把多個值組合成複合值。元組內的值能夠是任何類型,並不要求是相同類型的。

1.三、swift的不足:

  • - 版本不穩定
  • - 公司使用比例不高,使用人數比例偏低
  • - 有些語法其實能夠只規定一種格式,沒必要這樣也行,那樣也行。像Go同樣禁止一切(Go有點偏激)耍花槍的東西,同一個規範,方便團隊合做和閱讀他人代碼。

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

從數據結構角度,Golang和Swift對比,有何優缺點?面試

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

2、swift類(class)和結構體(struct)的區別

區別 class struct
定義屬性用於存儲值
定義方法用於提供功能
定義附屬腳本用於訪問值
定義構造器用於生成初始化值
經過擴展以增長默認實現的功能
遵照協議以對某類提供標準功能
是否能夠繼承
是否能夠引用計數
類型轉換
析構方法釋放資源

2.1 定義

class Class {
    // class definition goes here
}

struct Structure {
    // structure definition goes here
}
複製代碼

2.2 值 VS 引用

一、結構體 struct 和枚舉 enum 是值類型,類 class 是引用類型。 二、String, ArrayDictionary都是結構體,所以賦值直接是拷貝,而NSString, NSArrayNSDictionary則是類,因此是使用引用的方式。 三、structclass 更「輕量級」,struct 分配在棧中,class 分配在堆中。swift

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類型

使用classbash

  • - 須要繼承
  • - 被遞歸調用的時候(參考鏈表的實現,node選用class而不是struct
  • - 屬性數據複雜
  • - 但願引用而不是拷貝

參考連接1:類和結構體 參考連接2:官方文檔

3、Objective-C中的protocol與Swift中的protocol的區別

相比於OCSwift 能夠作到protocol協議方法的具體默認實現(經過extension)相比多態更好的實現了代碼複用,而 OC 則不行。

4、面向協議面向接口)與面向對象的區別

面向對象面向協議的的最明顯區別是對抽象數據的使用方式,面向對象採用的是繼承,而面向協議採用的是遵照協議。在面向協議設計中,Apple建議咱們更多的使用 值類型struct)而非 引用類型class)。這篇文章中有一個很好的例子說明了面向協議面向對象更符合某些業務需求。其中有飛機、汽車、自行車三種交通工具(均繼承自父類交通工具);老虎、馬三種動物(均繼承父類自動物);在古代馬其實也是一種交通工具,可是父類是動物,若是馬也有交通工具的功能,則:


若是採用面向對象編程,則須要既要繼承動物,還要繼承交通工具,可是父類交通工具備些功能馬是不須要的。因而可知繼承,做爲代碼複用的一種方式,耦合性仍是太強。事物每每是一系列特質的組合,而不僅僅是以一脈相承並逐漸擴展的方式構建的。之後慢慢會發現面向對象不少時候其實不能很好地對事物進行抽象。

若是採用面向協議編程,馬只須要實現出行協議就能夠擁有交通工具的功能了。面向協議就是這樣的抽離方式,更好的職責劃分,更加具象化,職責更加單一。很明顯面向協議的目的是爲了下降代碼的耦合性

總結:

面向協議相對於面向對象來講更具備可伸縮性可重用性,而且在編程的過程當中更加模塊化,經過協議以及協議擴展替代一個龐大的基類,這在大規模系統編程中會有很大的便捷之處。

3.一、協議協議擴展基類有三個明顯的優勢

  • 一、類型能夠遵照多個協議可是隻有一個基類。 這意味着類型能夠隨意遵照任何想要特性的協議,而不須要一個巨大的基類。
  • 二、不須要知道源碼就可使用協議擴展添加功能。這意味着咱們能夠任意擴展協議,包括swift內置的協議,而不須要修改基類源碼。通常狀況下咱們會給特定的類而非類的層級繼承體系)添加擴展;可是若是必要,咱們依然能夠給基類添加擴展,使全部的子類繼承添加的功能。使用協議,全部的屬性方法構造函數都被定義在遵照協議的類型自身中。這讓咱們很容易地查看到全部東西是怎麼被定義初始化的。咱們不須要在類的層級之間來回穿梭以查看全部東西是如何初始化的。忘記設置超類可能沒有什麼大問題,可是在更復雜的類型中,忘記合理地設置某個屬性可能會致使意想不到的行爲。
  • 三、協議能夠被類、結構體和枚舉遵照,而類層級約束爲類類型。 協議協議擴展可讓咱們在更合理的地方使用值類型引用類型值類型的一個主要的區別就是類型是如何傳遞的。當咱們傳遞引用類型(class)的實例時,咱們傳遞的對原實例的引用。這意味着所作的任何更改都會反射回原實例中。當咱們傳遞值類型的實例時,咱們傳遞的是對原實例的一份拷貝。這意味着所作的任何更改都不會反射回原實例中。使用值類型確保了咱們老是獲得一個惟一的實例由於咱們傳遞了一份對原實例的拷貝而非對原實例的引用。所以,咱們能相信沒有代碼中的其它部分會意外地修改咱們的實例。這在多線程環境中尤爲有用,其中不一樣的線程能夠修改數據並建立意外地行爲

3.二、面向對象的特色

優勢:
  • - 封裝

數據封裝、訪問控制、隱藏實現細節、類型抽象爲類;

代碼以邏輯關係組織到一塊兒,方便閱讀;

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

  • - 繼承

代碼重用,繼承關係,更符合人類思惟

  • - 多態

接口重用,父類指針可以指向子類對象

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

向上轉型的好處是隱藏了子類類型,提升了代碼的擴展性。

多態的不足:
  • - 父類有部分public方法是子類不須要的,也不容許子類覆蓋重寫
  • - 父類有一些方法是必需要子類去覆蓋重寫的,在父類的方法其實也是一個空方法
  • - 父類的一些方法即使被子類覆蓋重寫,父類原方法仍是要執行的
  • - 父類的一些方法是可選覆蓋重寫的,一旦被覆蓋重寫,則以子類爲準
較好的抽象類型應該:
  • - 更多地支持值類型,同時也支持引用類型
  • - 更多地支持靜態類型關聯(編譯期),同時也支持動態派發(runtime)
  • - 結構不龐大不復雜
  • - 模型可擴展
  • - 不給模型強制添加數據
  • - 不給模型增長初始化任務的負擔
  • - 清楚哪些方法該實現哪些方法不需實現

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

一、動態派發的安全性(這應該是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 <NSObject>
- (void)func;
@end

@interface SafeObj : NSObject<SafeProtocol>
@end
@implementation SafeObj
- (void)func {
    
}
@end

@interface UnSafeObj : NSObject
@end
@implementation UnSafeObj
@end
複製代碼

Objective-CXcode7中,可使用帶泛型容器也能夠解決這個問題,可是隻是⚠️,程序運行期間仍可能因爲此問題致使的崩潰

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 -> ViewCotrollerUIViewController -> UITableViewController -> AnotherViewController) 的橫切面上。面向對象是一種不錯的抽象方式,可是確定不是最好的方式。它沒法描述兩個不一樣事物具備某個相同特性這一點。在這裏,特性的組合要比繼承更貼切事物的本質。

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

面向對象編程中,針對這種問題的幾種解決方案:

  • - 一、Copy & Paste

快速,可是這也是壞代碼的開頭。咱們應該儘可能避免這種作法。

  • - 二、引入 BaseViewController

看起來這是一個稍微靠譜的作法,可是若是不斷這麼作,會讓所謂的 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)。

上面的例子中,若是咱們有多繼承,那麼 ViewControllerAnotherViewController 的關係可能會是這樣的:

若是ViewControllerUITableViewController都實現了myMethod方法,則在AnotherViewController中就會出現菱形缺陷問題。

咱們應該着眼於寫乾淨並安全的代碼,乾淨的代碼是很是易讀和易理解的代碼。

5、編程實踐:基於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 面向協議編程入門

面向協議編程初探

面向協議(POP)以面向對象(OOP)

面向對象編程和麪向協議編程

面向協議與面向對象的區別

面向協議編程的一些思考

iOS 面向協議方式封裝空白頁功能

iOS 面向協議封裝全屏旋轉功能

LXFProtocolTool

淺談Swift和OC的區別

相關文章
相關標籤/搜索