[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 面向协议编程入门