前言
已经很久没有在上更新了, 最近一直在忙于开发业务, 实在没有太多时间来沉淀, 再机上比较懒, 所以就一直没有更新了. 好不容易看了一些和绘图性能相关的东西, 但是因为有的大牛已经讲的比较清楚了, 所以也不多赘述什么了, 有兴趣的可以去看YYKit作者写的iOS 保持界面流畅的技巧, 任何关注绘图性能的同学都应该仔细读读这一篇博客
原因
Swift目前已经到了2.1版, 总算是相对稳定了, 之前有关注, 但是并不打算快速切入, 毕竟只是初版, 之后改动的可能性也是很大的, 加之那个时候切入, 其他的小伙伴也不一定愿意接受, 到时候只能自己维护自己的代码, 还要去改别人的代码, 要切换思维, 还是很浪费时间且没有效率的事情.
稍微谈一下我被Swift吸引的地方吧, 一个是optional value, 一个是面向协议编程, 还有就是extension
我目前也在学习中, 资料是苹果自己出的资料包括iBooks里面的和WWDC的视频, 我也会跟着资料一篇篇讲下去, 讲一些我觉得比较关键的点或者是需要注意的地方. 到时候会持续更新, 欢迎大家一起探讨, 有误之处还请大家指正.
基本变化
- 语法风格完全脱离了ObjC, 与Java类似了
- 每一条语句不需要;结尾了, 但是结尾也不会报错, 大多数情况下;都没有什么用了, 还有就是对空格要求严格一些了, 有些地方必须要空格,不然会报错的
- 没有宏定义了
- 条件判断不需要括号包围了
- 增加了大量的关键字, 不用也可以, 但是用了会使得代码很简洁易懂
- 去掉了block, 引入了closure(闭包), 其实差不多的作用, 就是写法不同
- 没有了id类型, 换成了AnyObject
- switch的匹配更加灵活强大, 而不仅限于ObjC里面的整数, 而且对每个case都是自动添加break
...
还是不一一列举了, 毕竟是一门全新的语言, 一直举下去也没什么意义, 还是直接切入正题吧. PS: 大家多用playground来测试代码吧 还是很方便的, 就是代码多了会慢一点
基础篇(The Basics)
- Swift中用var来声明变量, let来声明常量. 导致了Swift里面只有Array类型, 没有MutableArray类型(其它的集合类型也是一样), 因为var则代表可变, let则代表不可变. 例如:
var varArray: Array = [AnyObject]() // 初始化一个空数组, 可以看到, AnyObject就代表着id
varArray.append(1) // 插入一个整数, 注意, 这里不需要@1, 在Swift Int也是一个对象(不是int)
varArray.append("2") // 字符串也不需要@了
let constArray: Array = [1,2,3] // 不可变数组
constArray.append(3) // error: 不可以向不可变的数组插入元素
类型推导: 声明变量如果赋了初值, 那么就可以省略掉后面的类型, 例如:
var number = 1 // 编译器通过 = 1来推导出number类型为Int变量名支持Unicode编码字符, 这个我觉得没什么用, 应该没有人会真的用吧?
打印函数: print, 与C++类似, 支持重名函数, 所以print有多种用法, 但是都比较容易懂, playground实验一下就好, 个人感觉还是很方便的, 例如:
let name = "Ryan"
print("My name is \(name)") // 直接用\(variable)的方式直接代入变量
之前说了分号(;)很少用了, 代表还是有用的地方的, 比如你要在一行里面执行多条语句,就用分号(;)分割
Bool值由YES,NO改为了更加常见的true和false, 同时整数0与非0不能再代表false和true了, 因为Int没有实现BooleanType协议, 这是苹果故意的, 因为你自己extension Int: BooleanType的话, 编译器会明确告诉你, 不允许这么干, 应该是苹果想开发者更显式的写明条件, 避免出现意外的错误
元组(Tuples)
元组是一个很棒的东西, 以前在写C语言的时候, 因为返回值只能返回一个东西, 所以还要特意传入一个变量的地址来写入额外的信息, 或者返回一个结构体, OC里面也差不多是这样的, Swift引入了元组就更加强大了, 可以返回多个值, 用()包起来就可以了, 我估计其实是编译器帮我们实现了一个结构体, 例子:
let http404Error = (404, "Not Found") // 默认用http404Error.0/.1来访问404和"Not Found", 如果要指定, 则let http404Error = (code:404, reason:"Not Found"), 当然.0和.1还是可用的.
// 通过数据的访问形式, 我们的确是可以猜测其实就是编译器自己实现了一个结构体
Tip:元组在用法可不止这些, 还可以写出最简单的交换2个变量的值的代码. 以前我们都是, t = a, a = b, b = t 这种类型的写法, 整型还有更简单的写法, 但是用元素就是一行代码:
(a,b) = (b,a)
- Optionals: (我还是保持原文吧, 翻译为可选类型总是感觉很奇怪).
这个东西其实和ObjC新引入nullable差不多, 就是告诉开发者这个东西可能是为nil, 你要好好判断一下, 如果不判断就用, 首先会有编译报错, 告诉你要用感叹号(!)来获得这个值(数据叫做Unwrapping), 然后你解决了这个问题, 但是它其实可能是为空的, 到时候就会导致runtime error, 直接给挂了. 所以, 苹果说Swift的安全, 这是其一, 例子:
var serverResponseCode: Int?
serverResponseCode = 1
//var code: Int = serverResponseCode // error: 要用!来取值
if convertedNumber != nil { // 先判断, 后使用
print("convertedNumber has an integer value of \(convertedNumber!).")
}
至于optional binding, 其实就是赋值一次, 省略了感叹号(!)但是加了一个if let(if let得到的是常量, 所以也会有if var的), 如: // 1.30更新, 增加if var
var possibleNumber = "a12"
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
正如官方而言, Optional value是一个黑盒子, 在使用之前不之前里面是有东西还是空的, 这也就是是为什么你要用!来取值, 不然你取的就是这个盒子.
// 1.25日添加:
Optional还有一种加感叹号(!)的用法, 这种用法让你直接回到了ObjC的写法上了, 不需要额外去拆包, 但是很可惜, Swift对nil发送消息是会crash的, 所以要谨慎, 毕竟这种写法是不安全的. 例如:
var aStr: String!
//aStr.characters.count //runtime error
aStr = "123"
aStr.characters.count // print 3
- 错误处理:
错误处理是一大块内容, 这里先点一下, 按照Swift的设计, 抛出异常在Swift里面应该会用的比ObjC更多, 一个函数通过throws来抛出异常, 调用函数的地方用do, try, catch来捕获, 如:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
至于为什么要加个do, 而不是Java里面的直接try+catch, 我觉得应该是异常处理其实是比较影响性能的, 所以ObjC是不推荐用异常来处理错误的, 因为能少try的语句尽量少try, 所以上面的例子中, 实际上只对makeASandwich()进行了try, 而eatASandwich()则没有, 这样可以让尽量少的代码在try里面, 提高性能. 哈, 个人猜测...
- 断言:
没什么特别的, 和之前的差不多, 例如:
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
值得关注的是, 到底什么时候该用断言, 什么时候不该用? 苹果的说法是:
a.下标越界的时候用
b. 如果传入了一个无效的参数, 不满足函数的需求
c. 传入了nil, 但是函数本身需要一个非nil的参数
assertion可以在开发阶段就把问题通过crash的方式暴露出来, 而不是出现奇奇怪怪的结果, 然后一步步断点去查. 当然, 记得在Xcode中设置, release打包中去掉断言crash的选项...
这一部分值得说道的差不多就这么多了, 具体可以参考苹果的文档
里面对数值类型做了很多有意思的介绍, 比如老外喜欢千分位, 1000000要写成1,000,000, 在代码里面1000000不能一眼就看出来是多少, 所以有这样的写法:
var oneMillion = 1_000_000, 估计是编译器看到数值类型有_就直接删掉了.
还有很多有意思的tip, 但是用的地方可能不多就不多说了, 大家自己去探索吧.
基本运算符(Basic Operators)
其实这一章更加简单, 基本上没有差别, 只是Swift和C++一样, 也有也运算符重载, 可以丰富很多写法.
这里只记录一些区别的地方:
- 赋值语句不再有返回值, 所以以前在ObjC里面写的
if (self = [super init])
{
// ...
}
在Swift里面还这么玩就报错了, 其实这是对原本要写==写成=的一种保护吧
- 三目运算符:
三目运算符不知道各位用的多不多, 我个人用的比较多, 比如我的block执行宏就是
#define RUN_BLOCK_IF_NONNIL(block,...) !(block)?:(block)(__VA_ARGS__);
这里对block进行为空判断, 为空则返回自身, 也就是nil, 否则传入参数执行之.
但是对于第一个参数返回自身省略的写法, Swift是这样的:
let defaultColorName = "red"
var userDefinedColorName: String? // defaults to nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
用??替换掉了?:
但是, 普通的用法, 也就是:前后都写的, 还是一样的
- 求余数(%):
在C和ObjC里面, %都只能对整数使用, 但是Swift进行了拓展, 可以对浮点数使用了:
8 % 2.5 // 结果为 0.5
- 范围操作符:
也就是表示范围的, 有两种写法: 1...3 代表1, 2, 3, 如果不想包含后面的数字, 就要写成1..<3. (话说之前的版本是用..来代表..<的, 在新版进行了修改)
除此之外, Swift专门有个Range类型来表示之:
var range: Range = 1...3 // 会被编译器描述成1..<4
其它的操作符基本上的一直的, 想要查看细节可以看[苹果文档](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID60)