2017年 5 月 23 日,历时 4 个小时又拍云 Open Talk 第 7 期 《iOS开发与前端技术分享》沙龙在成都天府软件园 A 区十分咖啡圆满结束。咕咚技术总监唐平麟在活动上作了题为《 iOS开发中Swift带来的改变和启示》的演讲,以下是分享实录:

Swift 介绍

Swift 是在 WWDC2014 苹果发布的全新开发语言,相比于 Object-C 静态语言,Swift 中增加 var 声名变量、let 赋值、func 定义函数和方法、不需要 main 函数等语法,使Swift在可读性上超越了Objective-C,比起Objective-C,Swift还多了许多现代编程语言特性:泛型、闭包、多个返回值、命名空间。

LLVM-CLang : Apple 在硬件行业翻身的关键

LLVM 的创始者 Chris Latter 是在 Apple 历史上,软件开发层面重要的一个人,他是 LLVM 的发起人,Clang 编辑器的作者,现在是Apple 开发者工具部门的主管,领导 Xcode、instruments 和编译器团队,从2010年7月开始主导开发Swift编程语言,Swift 是克里斯在 LLVM 和 Clang 之后第三个伟大的项目!卖桃君池院长对他的评价是:

克里斯可以说是天才少年和好学生的代名词,他在2000年本科毕业之后,继续攻读计算机硕士和博士。但克里斯并不是宅男,学习之余他手捧「龙书」游历世界,成为德智体美劳全面发展的好学生。之后就是一篇又一篇的发表论文,硕士毕业论文即提出了一套完整的运行时编译思想,奠定了 LLVM 的发展基础,读博期间 LLVM 编译框架在他的领导下得到了长足的发展,已经可以基于 GCC 前端编译器的语义分析结果进行编译优化和代码生成,所以克里斯在2005年毕业的时候已经是业界知名的编译器专家了。

注:很多计算机专业的大学生经常问我在大学里学点什么好,看看克里斯就行了。以目前的科技信息开放程度,如果你在自己感兴趣的领域里用心耕耘,再加上那么一点点天分,毕业时成为某一个专有领域的专家应该不是问题。那时就不是你满世界去找工作了,而是工作满世界来找你!

克里斯毕业的时候正是苹果为了编译器焦头烂额的时候,因为苹果之前的软件产品都依赖于整条 GCC 编译链,而开源界的这帮大爷并不买苹果的帐,他们不愿意专门为了苹果公司的要求优化和改进 GCC 代码,所以苹果一怒之下将编译器后端直接替换为 LLVM,并且把克里斯招入麾下。克里斯进入了苹果之后如鱼得水,不仅大幅度优化和改进 LLVM 以适应 Objective-C 的语法变革和性能要求,同时发起了 CLang 项目,旨在全面替换 GCC。这个目标目前已经实现了,从 OS X10.9和 XCode 5开始,LLVM+GCC 已经被替换成了 LLVM+Clang。

摘自苹果新贵 Swift 之前世今生

苹果为什么要做LLVM 传统编译器通常分为3个部分:

  • 前端(frontEnd)
  • 优化器(optimizer)
  • 后端(backEnd)

在编译过程中,前端主要负责词法和语法分析,将源代码转化为抽象语法树;优化器则是在前端的基础上,对得到的中间代码进行优化,使代码更加高效;后端则是将已经优化的中间代码转化为针对各个平台的机器代码。Clang 则是以 LLVM 为后端的一款高效易用,并且与 IDE 结合很好的编译前端。 Clang 可以用来编译 C,C++,Objective-C 等语言。在09年,10年的时候,GCC 协议发布,苹果当时资助了不少钱,Clang 和 GCC 相比,编译速度更快,编译产出更小,出错提示更友好,并且 Clang 采用的是 BSD 协议。这是苹果资助LLVM、FreeBSD 淘汰 GCC 换用 Clang 的一个重要原因。

提高效率的Swift 范型和特性

Swift 中并没有宏,但 Swift 中任然存在简单的条件编译的功能,比如我们需要根据不同的平台定义一个变量,可以使用下面示例:

os():  OSX, iOS

arch():  x86_64, arm, arm64, i386


if os(OSX)

 typealias View = NSView

else

 typealias View = UIView

 endif


在 Build Setting 中的 Swift Compiler - Custom Flags 的 Other Swift Flag 中可以添加自定义的编译符号

image.png

if os(Testing)

baseURL = “http://codoon.com/api

else

baseURL = “http://test.codoon.com/api

endif


SwiftyJson

image.png

APP会因为API的数据而不稳定,且OC在处理器上并不完善,程序可能会因为数据不稳定而坏掉。

Swift如何安全的取出数据 一个Json数据:

[

  {

    "user": {"name": "Jack"}

  }

  {

    "user": {"name": "Tom"}

  }

]

按照 Swift1.2 的方法安全的取出数据:

if let statusesArray = object as? [AnyObject],

    let status = statusesArray[0] as? [String: AnyObject],

    let user = status["user"] as? [String: AnyObject],

    let username = user["name"] as? String {

        println(username)

}

Swift 1.2更简单一点的方法:

if let username = (((object as? [AnyObject])?[0] as? [String: AnyObject])?["user"] as? [String: AnyObject])?["name"] as? String {

    println(username)

}

使用 SwiftyJSON:


if let userName = json[0]["user"]["name"].string{

    println(username)

}

使用 SwiftyJSON 后,我们可以用简单的方法去取数据。至于 SwiftyJSON 为什么可以这么简单而安全地取出 JSON 中的数据,主要是因为 Swift 提供了 OC 之前没有的东西,里面提供了大量的数据,所以我们才写出这样的内容。 

明星特性 -- Chained calls

给大家介绍一个我觉得 Swift 中非常费重要的特性 Chained calls。先举这么个例子,在 OC 中如果我要发起一个网络请求,我会用到 AFNetworking 库,我需要先建立一个 request, 然后发送请求,将请求修饰器和结果处理以 block 的形式传递过去,这样的代码看起来是这样的:

bash AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; [manager GET:@"http://abc.com/get" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"JSON: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }];


这样的代码看里面非常的惹眼,而且不便于理解整个流程。这个问题在Swift就迎刃而解,我取到数据之后的处理,代码就像之前讲到的,一目了然:


bash Alamofire.request(.GET, "http://abc.com/get", parameters: ["foo": "bar"]) .response { (request, response, data, error) in println(request) println(response) println(error) }


这里还有一个例子,大家习惯性的在操作UI的时候,或者是以前的过程一个语句一个语句地写,代码非常冗余,在 Swift 中其实我们可以把简单的 UIKit 提供的函数分装一下,得到一个 var button,首先去设属性,设了属性,当有突发事件的时候,我就改变一下颜色,就不用申明。在 OC 中我们经常写八九行代码完全这样的事情,并且会散落在各个地方,这个代码如果是过于复杂的话,如果出问题,你就不知道问题出在哪儿。这也是未来的一个方向吧,包括之前讲的 React 也是这样的,为什么把函数代码尽可能的写在一起,出来的时候就绑定在一起,包括一些事物,一些定向处理,管理。我不用去给这个事件再命名。在我看来编程里面难的就是命名,我们写个函数在里面是很烦琐的过程,我还得想一个好听的名字,少去命名,多去绑定。

bash var button = UIButton(frame:buttonFrame) .now {button in button.setTitle("Button", forState: UIControlState.Normal) }.when(UIControlEvents.TouchUpInside) { $0.backgroundColor = UIColor.blackColor() }

函数式编程

函数式编程是一种编程范式,OC里也可以函数式编程,包括 block 特性都给函数式编程增加了一米阳光。 Swift 写函数会比较简单,函数、对象和结构体能代表任意的东西,比如我们写命名编程,会解决特点场景下的优化。 非函数式编程实现取10以内奇数的和:

bash var sum = 0 var odds = [Int]() for i in 1...10 { if i % 2 == 1 { odds.append(i) sum += i } } println(sum)

用函数式编程取10以内奇数的和:

let sum = Array(1...10)

    .filter {$0 % 2 == 1}

    .reduce(0) {$0 + $1}

println(sum)

函数式编程能写的,其实别的方式也能,不过比其它更简单,可读性更高,而且代码量更小了。

Github 中比较不错的一些 Swift 项目:

  • Alamofire
  • SwiftyJSON
  • Design-Patterns-In-Swift
  • Dollar.swift
  • ReactiveCocoa

Swift的不足

当在Swift中需要用到Object-C的API时,需要定义一个叫做指针的结构体,这种结构体操作起来会非常麻烦,因为有5,6种结构体散落在C的API里面。在这种情况下,会耗费很多找资料的时间,还不如用OC直接写。