面试中经常被问到Objective-C与Swift的区别,其实区别还是很多的,重点整理一下个人觉得很重要的:面向协议编程。
1.1、swift和OC的共同点:
1.2、swift的优点:
1.3、swift的不足:
Swift 跟 JavaScript 有什么相同和不同点?
从数据结构角度,Golang和Swift对比,有何优缺点?
iOS——Objective-C与Swift优缺点对比
区别 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
相比于OC,Swift 可以做到protocol协议方法的具体默认实现(通过extension)相比多态更好的实现了代码复用,而 OC 则不行。
面向对象和面向协议的的最明显区别是对抽象数据的使用方式,面向对象采用的是继承,而面向协议采用的是遵守协议。在面向协议设计中,Apple建议我们更多的使用 值类型 (struct)而非 引用类型 (class)。这篇文章中有一个很好的例子说明了面向协议比面向对象更符合某些业务需求。其中有飞机、汽车、自行车三种交通工具(均继承自父类交通工具);老虎、马三种动物(均继承父类自动物);在古代马其实也是一种交通工具,但是父类是动物,如果马也有交通工具的功能,则:
如果采用面向对象编程,则需要既要继承动物,还要继承交通工具,但是父类交通工具有些功能马是不需要的。由此可见继承,作为代码复用的一种方式,耦合性还是太强。事物往往是一系列特质的组合,而不单单是以一脉相承并逐渐扩展的方式构建的。以后慢慢会发现面向对象很多时候其实不能很好地对事物进行抽象。
如果采用面向协议编程,马只需要实现出行协议就可以拥有交通工具的功能了。面向协议就是这样的抽离方式,更好的职责划分,更加具象化,职责更加单一。很明显面向协议的目的是为了降低代码的耦合性。
总结:
面向协议相对于面向对象来说更具有可伸缩性和可重用性,并且在编程的过程中更加模块化,通过协议以及协议扩展替代一个庞大的基类,这在大规模系统编程中会有很大的便捷之处。
3.1、协议和协议扩展比基类有三个明显的优点:
3.2、面向对象的特点
优点:
数据封装、访问控制、隐藏实现细节、类型抽象为类;
代码以逻辑关系组织到一起,方便阅读;
高内聚、低耦合的系统结构
代码重用,继承关系,更符合人类思维
接口重用,父类指针能够指向子类对象
当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。
向上转型的好处是隐藏了子类类型,提高了代码的扩展性。
多态的不足:
较好的抽象类型应该:
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;@end@interface SafeObj : NSObject@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'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() { }}复制代码
在面向对象编程中,针对这种问题的几种解决方案:
快速,但是这也是坏代码的开头。我们应该尽量避免这种做法。
看起来这是一个稍微靠谱的做法,但是如果不断这么做,会让所谓的 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()复制代码
3、菱形问题
多继承中,两个父类实现了相同的方法,子类无法确定继承哪个父类的此方法,由于多继承的拓扑结构是一个菱形,所以这个问题有被叫做菱形缺陷(Diamond Problem)。
上面的例子中,如果我们有多继承,那么 ViewController 和 AnotherViewController 的关系可能会是这样的:
如果ViewController 和UITableViewController都实现了myMethod方法,则在AnotherViewController中就会出现菱形缺陷问题。
我们应该着眼于写干净并安全的代码,干净的代码是非常易读和易理解的代码。
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) }}