202 - Swift 的核心是什么?

让我们来思考一个问题,就是 Swift 的核心是什么?

不知道大家有没有看过 WWDC 2015 的视频,其中有一个编号为 408 的视频解释了这个问题,下面是视频链接:Protocol-Oriented Programming in Swift

视频中介绍了从 OOP(面向对象编程) 到 POP(面向协议编程)的转变过程。

Swift is a Protocol-Oriented Programming Language Swift 是一门面向协议 (POP) 开发的语言

我说一下我的体会吧,我刚开始做 iOS 开发的时候使用 OC 来开发的,后来学习了 Swift,当时也是有一搭没一搭的学,了解了一下 Swift 的基本语法,感觉还是很简单的,因为当时 Swift 也是刚出来,很不稳定,1.0 到 2.0,甚至都不兼容,所以也就没有选择使用 Swift 来开发。

直到去年,也就是16 年,在 2.2 版本出来之后,感觉 Swift 还算比较稳定了,才决定使用 Swift 来开发一个简单的项目,当时决定模仿一个 app 来练手,也就是现在在我的 github 上的那个项目了。但是现在看来,那个项目写的不是很好,虽然是使用 Swift 来开发的,但是并没有按照 Swift 的标准来写 Swift 的项目,反而是以 OC 的习惯来写 Swift,也就是还是按照面向对象的思想来写 Swift,虽然也能写出可以运行的项目,但是面向对象的思想就和 Swift 的编程思想还是有本质的区别的。这里我不想着重介绍关于 OC 这门编程语言,毕竟我们现在是用 Swift 来开发的,但是有些东西还是要说明一下,首先面向对象编程的特征是 class,继承,封装和多态,其实 OC 还不能说是一门纯面向对象的的语言,只能说 OC 是 C 语言的超集,或者说是 C 语言的扩展,在 C 语言的基础上增加了面向对象的思想。但是在 Swift 里就不一样了,Swift 里 class 并不是最重要的。

我前面说了 Swift 是面向协议的编程,那么究竟什么是面向协议编程呢?

要回答这个问题,我们可以参考一下刚刚提到的面向对象编程,在面向对象编程里,是从一个 class 开始的,那要是照这样说,在面向协议编程里就是从一个 protocol 了吗?这样解释对不对呢?我们可以在刚刚提到视频里找找答案,如果看过上面的视频,你会发现在上面的视频中 Apple 自己都说:

"从一个 protocol 开始,别从 class 开始。" ——Dave Abrahams: 毁你三观教授

protocol 就是协议的意思。当然,可以从protocol 开始,但是从 protocol 开始了之后,该怎么做呢?

是的,这也是我们该思考的问题,我这里不会太着重去介绍 Swift 的基础,因为我默认看我视频的同学都已经掌握了 Swift 的基础了,所以关于 protocol 的概念我也不在详细介绍了,回到我们刚才的问题,现在我们已经有了 protocol,接下来我们要做的就是使用非常强大的 extension 了,额…,关于 extension 的概念我也不再详细介绍了,如果感觉基础不好的同学可以先去看一下基础,然后再来看我的视频吧,关于 extension,可以为现有的 class,struct,enum,protocol 添加新功能,注意刚刚我提到了 protocol,所以我们先现在可以在 protocol 的extension 里添加任何你需要添加的东西了。

那好,功能也添加了,那怎么该怎么使用这个 protocol 呢?

这也是个问题,让我们再分析一下,protocol 不同于 class 或者 struct,因为后两者可以各自调用它们的类型方法或者实例方法,但是 protocol 却不能直接使用,也不能实例化,既然都不行,那该怎么做啊?别着急,既然不能直接用,那我们就要考虑用上面提到的 class 或者 struct 了,那我们该用哪个呢?我们先来看一张图:

[图片上传失败...(image-fd804b-1517807204933)]

这张图是我在网上找到的一篇文章中的截图,下面是文章地址: 不要用子类!Swift的核心是面向协议 ,虽然这篇文章是2015年的文章了,不过还是推荐大家看一下。在上面的图中,可以看出在 Swift 的标准库中,仅有 4 个class,其余下的有 87 个 struct 和 8 个 enum 的实例共同构建了 Swift 功能的核心。如今已经过去两年,我想 struct 的数量应该更多了。既然 Swift 里用了这么多 struct,为什么我们不试试用 struct 呢?

我们前面也说过了 class 是面向对象里的东西,那我们试试用 struct,现在可以新建一个 struct,然后让它遵守我们的 protocol 就可以了,之后就可以实例化一个 struct,接着就可以用 struct 调用 protocol 里的方法或者属性了。

听上去还不是错的,但是总感觉是不是有点太麻烦了,要是按照上面说的,我们直接创建一个 struct 不就完了嘛,还要 protocol 干什么,这么说听上去也没有问题,当然在开发中也是可以的.

但是我们还要考虑一个问题,在实际开发中我们是不是只有 struct 呢?

当然不是,因为我们还要和 cocoa 框架打交道,说到 cocoa 框架,我们还要提一下 UIKit 这个框架,这是 iOS 开发中一个十分重要的框架,但是由于历史关系,为了兼容 OC,UIKit 里的类都是继承自 NSObject 的,也就是说都是 class 类型的,比如在开发中有几十个控制器都继承自某个自定义的基类,就会把基类的所有的方法也继承下来,但是这些方法对每一个子类都有用吗?答案肯定是否定的。所以,既然子类不需要,何必要继承父类的方法呢?自己的方法应该由自己决定才对的,而现在是基类帮着子类决定了它的方法。

所以这样就引出了 protocol,让自己的类实现自己所要遵守的 protocol,这里我说的并不是某一个 class,我这里指的是有那么几个 class 都要实现功能的时候,选择用 protocol 是个不错的选择,而且还可以把几个方法抽象成一个方法,需要的 class 只需要遵守这个 protocol 就可以了。这样解释可能不太清楚,我举一个栗子。

当我自定义 UIView 的时候,我想让 view 从 xib 加载,那么我就需要在每个类里都写一个从 xib 加载的类方法,如下代码:

static func classMethodCreateView() -> MyCustomView {
    return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.last as! MyCustomView
}

这样在每个代码都写一,很是麻烦,有什么方法可以简单一点吗?方法当然是有的,可以做一下优化,如下代码:

protocol LoadNibProtocol {}

extension LoadNibProtocol where Self: UIView {
    /// 提供加载 Xib 方法
    static func loadViewFromNib(name: String? = nil) -> Self {
        return Bundle.main.loadNibNamed(name ?? "\(self)", owner: nil, options: nil)?.last as! Self
    }
}

接下来让需要从 xib 加载的 view 遵守 LoadNibProtocol 协议就可以了,是不是简单了许多呢?

上面只是 protocol 的一个简单应用,在后面的项目中,我会介绍其他用法,这里就不再过多说明了,关于协议暂时先介绍这么多。

下面还有一个问题,需要思考一下,就是 Swift 里既然有 class 和 struct,那么他们的区别是什么呢?

  • 我想大多数人的第一反应应该是 struct 是值类型 class 是引用类型,也就是说 struct 的实例在被赋予变量或者常量或者被函数调用时都会被复制,但是 class 的实例会被引用,引用的就是已经存在的实例本身而不是复制。还可以这样理解 struct 的复制相当于在内存上又开辟了一块内存空间,和之前的 struct 没有关系了,我个人感觉也可以理解成深拷贝,而 class 则是创建一个指针,指向的还是原来的内存地址,可以理解成浅拷贝。
  • class 可以继承,struct 不能继承,某些需要继承的地方还是需要用 class,不能用 struct。
  • struct 类型方法要加 static修饰,class类型方法要加 class 修饰。
  • struct 有默认的初始化方法,class 需要指定变量的初始值。 下面代码关于 class 和 struct 的在初始化的时候的一些区别。
struct MyStruct1 {
    var text: String
    var tip: Int
}

struct MyStruct2 {
    var text: String = "MyStruct2"
    var tip: Int = 2
}

struct MyStruct3 {
    var text: String
    var tip: Int
    init(text: String, tip: Int) {
        self.text = text
        self.tip = tip
    }
}

class MyClass1 {
    var text: String = "MyClass1"
    var tip: Int = 11
}

class MyClass2 {
    var text: String
    var tip: Int
    init() {
        text = "MyClass2"
        tip = 22
    }
}

class MyClass3 {
    var text: String
    var tip: Int
    init(text: String, tip: Int) {
        self.text = text
        self.tip = tip
    }
}

let myStruct1 = MyStruct1(text: "MyStruct1", tip: 1)
let myStruct2 = MyStruct2()
let myStruct3 = MyStruct3(text: "MyStruct3", tip: 3)
let myClass1 = MyClass1()
let myClass2 = MyClass2()
let myClass3 = MyClass3(text: "MyClass3", tip: 33)

还有一点,就是关于 struct 和 class 的性能差异,可以阅读下面的文章:理解Swift中struct和class在不同情况下性能的差异,文章介绍的很详细,我这里也不再详细介绍了。

上面是我对 struct 和 class 做的简单说明,以及 Swift 面向协议编程的简单说明,如果还觉得意犹未尽,或者想了解更多内容,请自行去网上找找相关文章。

说了这么多,最后还是希望你们能明白 Swift 是面向协议的编程, 在开发过程中请多使用 struct 和 protocol,当你没有选择的时候再使用 class。

新增一篇参考文章:面向协议的 MVVM 架构介绍。这篇文章也比较早了。

下面我们就继续写代码吧。

首先新建两个 Swift 文件,一个命名为 MyCellModel.Swift,作为我的界面 cell 的模型。

另一个命名为 NetworkTool.Swift,作为网络请求的相关文件。

然后在 Podfile 添加我们需要的第三方框架,分别是 AlamofireSwiftyJSONHandyJSON。 如下代码:

target 'News'  do
    use_frameworks!
    pod 'Alamofire', '~> 4.5.0'  # 数据请求 https://github.com/Alamofire/Alamofire,同 AFNetworking
    pod 'HandyJSON', '~> 1.7.2'         # JSON序列化/反序列化库 https://github.com/alibaba/HandyJSON/
    pod 'SwiftyJSON'                    # json 解析 https://github.com/SwiftyJSON/
end

默认生成的测试 target 先不需要,可以删掉。

关于上面的第三方框架可以去 github 看一下他们的介绍和用法,我这里就不详细说明了,看我是怎么写的就可以了,跟着我写,写着写着就知道怎么用了。

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=nmhhdqxcpeov

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LinkedBear的个人空间

设计模式笔记(一)——设计模式的引入与三大工厂模式

设计模式(Design Pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总...

662
来自专栏walterlv - 吕毅的博客

应该抛出什么异常?不应该抛出什么异常?(.NET/C#)

2018-02-04 13:25

1762
来自专栏JavaEdge

设计模式实战 - 简单工厂

最可能给八卦炉下达什么样的生产命令呢? 应该是给我生产出一个黄色人种(YellowHuman类) 而不会是给我生产一个会走、会跑、会说话、皮肤是黄色的人种 ...

1165
来自专栏程序员互动联盟

【编程入门】C语言堆栈入门——堆和栈的区别

在计算机领域,堆栈是一个不容忽视的概念,我们编写的C语言程序基本上都要用到。但对于很多的初学着来说,堆栈是一个很模糊的概念。堆栈:一种数据结构、一个在程序运行时...

4436
来自专栏Jimoer

Java设计模式学习记录-模板方法模式

模板方法模式,定义一个操作中算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

1694
来自专栏牛客网

金山WPS,C++研发工程师,一面

【每日一语】人们常常会欺骗你,是为了让你明白,有时候,你唯一应该相信的人就是你自己。——《千与千寻》

842
来自专栏后端技术探索

非Java程序员竟鲜有人真正理解DI和IOC

小编在后端圈也算是阅人无数了, 发现一个现象,Java程序员对于面向对象语言的基础知识整体掌握比较扎实,而类似PHP,Python的初级甚至中级程序员就比较薄弱...

1852
来自专栏极客猴

Python 中各种时间类型的转换

我们编码过程中经常需要获取当前时间。当然, 这也离不开对时间类型进行转换运算。本文主要讲解 Python 各种时间类型之间的转换。

892
来自专栏小詹同学

为什么你的Python代码质量如此不堪……

作者:笑虎(Python爱好者,关注爬虫、数据分析、数据挖掘、数据可视化等) 原文链接:http://codebay.cn/post/7953.html

1594
来自专栏惨绿少年

Python入门篇

3704

扫码关注云+社区

领取腾讯云代金券