从 Objective-C 到 Swift —— Swift 糖果 【已翻译100%】
Swift带来很多确实很棒的特性,使得很难再回到Objective-C。主要的特性是安全性,不过这也被看成是一种额外副作用。
带类型接口的强型别
Swift有强型别,这意味着除非你要求,不然Swift不会为你做类型之间的转换。所以,例如你无法把Int型赋给Double型。你不得不首先转换类型:
let i: Int = 42 let d: Double = Double(i)
或者你必须给Double类扩展一个方法用来转换Int型:
extension Double { func __convert(i: Int) -> Double { return Double(i) } } let another_d: Double = i
强型别对于安全性是非常非常有利的。但是,如果它没有为你给类型接口添加许多类型信息的话,它也可能变成有一点令人生畏的事情,有点像是在写脚本语言。
let ary = ["Hello", "world"] // NOTE: 'ary' is of type String[] or Array<String> for s in ary { // NOTE: 's' is of type String print(s + " ") }
如果你想要创建一个包含很多类型(无共同祖先)的数组,你应该用枚举(它可以包含值,见如下)。如果你想要它能够包含所有值,你可以用Any型。如果想让它包含任何Objective-C的类型,那就用AnyObject型。
请注意类型接口不会在申明函数的时候为你添加类型。你必须明确地说明你所申明函数的类型。
Blocks
Swift 中的Blocks很像Objective-C中的Blocks, 不过有两点不同: 类型推断和避免weakify dance.
对于类型推断,你不必每次写block时都包含完整类型信息:
sort([2,1,3], { (a: Int, b: Int) -> Bool in return a < b }) // Using Type Inference // Using the Trailing Closures feature sort([2,1,3]) { a, b in return a < b } // Implicit 'return' for single-expression blocks sort([2,1,3]) { a,b in a<b } // Shorthand Argument Names sort([2,1,3]) { $0 < $1 } // Operators are functions, and functions are blocks too! let sorted: Int[] = sort([2,1,3], <)
访问 Closures 了解更多blocks信息。
除此之外,Objectvie-C 的weakify dance有点容易,只需在block的开始处加上 [unowned self] 或 [weak self] 即可。
class CallbackTest { var i = 5 var callback: (Int -> ())? // NOTE: The optional callback takes an Int deinit { // NOTE: This is like -dealloc in Objective-C println("Deinit") } } var obj = CallbackTest() obj.callback = { [unowned obj] // NOTE: Without this, deinit() would never be invoked! a in obj.i = a }
请注意Introduction post文章中介绍了Optional(像上面的callback)。
请参考 ARC 章节来了解更多关于Swift中ARC的信息。
强劲的Enumerations
Swift中的枚举比Objective-C中的有很大提高。
**修复 Enums
**
Apple一直提倡显示的提供枚举类型的大小,不过被Objective-C搞乱了:
// Apple recommended enum definition in Objective-C typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { UIViewAnimationCurveEaseInOut, UIViewAnimationCurveEaseIn, UIViewAnimationCurveEaseOut, UIViewAnimationCurveLinear }; // Previous Apple recommended enum definition in Objective-C. No link between // enum values and theUIViewAnimationCurve type. typedef enum { UIViewAnimationCurveEaseInOut, UIViewAnimationCurveEaseIn, UIViewAnimationCurveEaseOut, UIViewAnimationCurveLinear }; typedef NSInteger UIViewAnimationCurve; // Traditional enum definition in Objective-C. No explicit fixed size. typedef enum { UIViewAnimationCurveEaseInOut, UIViewAnimationCurveEaseIn, UIViewAnimationCurveEaseOut, UIViewAnimationCurveLinear } UIViewAnimationCurve;
Swift中的修复:
enum UIViewAnimationCurve : Int { case EaseInOut case EaseIn case EaseOut case Linear }
拓展Enums
Enums 在Swift中更进一步,只作为一个独立的选项列表。你可以添加方法(以及计算属性):
enum UIViewAnimationCurve : Int { case EaseInOut case EaseIn case EaseOut case Linear func typeName() -> String { return "UIViewAnimationCurve" } }
使用类型拓展,你可以向枚举中添加任何你想要的方法:
extension UIViewAnimationCurve { func description() -> String { switch self { case EaseInOut: return "EaseInOut" case EaseIn: return "EaseIn" case EaseOut: return "EaseOut" case Linear: return "Linear" } } }
向Enums中添加值
Swift中的枚举跟进一步,允许每一个独立的选项都有一个对应的值:
enum Shape { case Dot case Circle(radius: Double) // Require argument name! case Square(Double) case Rectangle(width: Double, height: Double) // Require argument names! func area() -> Double { switch self { case Dot: return 0 case Circle(let r): // Assign the associated value to the constant 'r' return π*r*r case Square(let l): return l*l case Rectangle(let w, let h): return w*h } } } var shape = Shape.Dot shape = .Square(2) shape = .Rectangle(width: 3, height: 4) // Argument names required shape.area()
如果你喜欢,你可以把它当做一个安全的union类型。或者只用枚举应该做的事情。
Enumerations 文章介绍了更多关于Apple对此的看法。
Swift Switch语句
就像你看到的,Swift中switch语句有很多优化。
隐式的fall-through行为已经改为了显示的:
var (i, j) = (4, -1) // Assign (and create) two variables simultaneously switch i { case 1: j = 1 case 2, 3: // The case for both 2 and 3 j = 2 case 4: j = 4 fallthrough case 5: j++ default: j = Int.max // The Swift version of INT_MAX }
就像前面看到的,Switch 语句可以访问枚举的关联值,不过它还可以做更多:
var tuple: (Int, Int) // Did I mention that Swift has tuples? :-) var result: String tuple = (1,3) switch tuple { case (let x, let y) where x > y: result = "Larger" case (let x, let y) where x < y: result = "Smaller" default: result = "Same" }
甚至可以使用String:
var s: String = "Cocoa" switch s { case "Java": s = "High caffeine" case "Cocoa": s = "High sugar" case "Carbon": s = "Lots of bubbles" default: () }
另外,如果你觉得他可以使你的代码更可读,你可以重载~=操作符来改变switch语句的行为。
func ~=(pattern: String, str: String) -> Bool { return str.hasPrefix(pattern) } var s = "Carbon" switch s { case "J": s = "High caffeine" case "C": s = "No caffeine" default: () }
你可以从 Conditional Statements 这篇文章中了解更多关于switch语句的知识。
类与结构体
类似于C++,Swift的类与结构体初看是一样的:
class Apple { var color = "green" // Property declaration init() {} // Default initializer init(_ color: String) { // '_' means no argument name self.color = color } func description() -> String { return "apple of color \(color)" } func enripen() { color = "red" } } struct Orange { var color = "green" init() {} init(_ color: String) { self.color = color } func description() -> String { return "orange of color \(color)" } mutating func enripen() { // NOTE: 'mutating' is required color = "orange" } } var apple1 = Apple() var apple2 = apple1 // NOTE: This references the same object! apple1.enripen() apple2.description() // Result: "apple of color red" var orange1 = Orange() var orange2 = orange1 // NOTE: This makes a copy! orange1.enripen() orange2.description() // Result: "orange of color green"
主要的不同点在于类是(和块相似的)引用类型,而结构体是(和枚举相似的)数值类型。所以两个变量能够指向同一个(类的)对象,而把一个结构体赋给另外一个变量则必须做一个此结构体的(缓慢的)拷贝。关键词'mutating'告诉调用者enripen()方法不能被常结构体调用。把一个常引用mutating给一个类对象则没有问题。
Swift中大多数内建类型实际上都是结构体。甚至Int型也是。通过点击Cmd你能够看到内建类型的申明,比如Int型的Swift(或者Playground)源码。数组和词典类型也是结构体,但是数组在某些方面表现得像是引用类型:赋值数组并不拷贝每一个元素,实际上你可以更新常数组只要元素的个数保持不变。
let array1 = [1, 2, 3] let array2 = array1 // A copy, but references the same elements! array1[1] = 5 // Legal, as it doesn't modify the struct but a referenced element array2 // Result: [1, 5, 3]
在苹果的文档中,你可以读到更多关于Collection Types的内容。
对象的生命周期
另一个类与结构体的不同是类可以被子类化。 类和结构体都可以被拓展,并且实现protocol,但是只用类可以继承其他类。
class Pineapple : Apple { init(color: String) { super.init(color) } convenience init() { self.init("green") } convenience init(ripe: Bool) { self.init() if ripe { color = "yellow" } else { color = "green" } } deinit { println("Pineapple down") } override func description() -> String { return "pine" + super.description() } override func enripen() { color = "yellow" } }
就像你看到的,Swift为继承添加了一点更有趣的需要学习的东西。对于初学者来说,你需要清除你覆盖父类中某个方法的意图。如果你想阻止子类覆盖一些东西,你可以在一个单独声明或整个类的前面加上@final属性。阅读 Apple’s documentation了解更多。
初始化
Swift的对象分两步进行初始化: 首先对象必须是有效的,然后它能被替换。
class ChildShoe { var size: Double // Uninitialized properties are not allowed unless taken care of in init() init(foot_size: Double) { size = foot_size // First make the object valid addGrowthCompensation() } func addGrowthCompensation() { size++ } }
使对象有效必须要调用一个超级类指定的init()方法。类可以同时拥有指定的以及便捷的(用关键词'convenience'标记)初始化方法。便捷初始化方法调用同一个类中的其他初始化方法(最终还是一个指定的初始化方法),而指定的初始化方法调用超级类的初始化方法。
如果你给所有的超级类指定初始化方法添加初始化方法,你的类也会自动继承所有便捷初始化方法。如果没有添加任何指定的初始化方法,你的类则继承超级类的所有(指定的和便捷的)初始化方法。
深入阅读请参见Initialization。
类型转换
类之间的转换,特别是向下转换,你可以使用"is","as?"和"as"关键词:
let apple: Apple = Pineapple()
let exotic: Bool = apple is Pineapple
let pineappleOption: Pineapple? = apple as? Pineapple
let pineapple: Pineapple = apple as Pineapple // NOTE: Throws if not!
if let obj = apple as? Pineapple { // If executed, 'obj' is a Pineapple
"sweet"
}
想了解更多这方面内容请参见Type Casting一章.
泛型
泛型是Swift的一个加分的特点。他们看起来有一点像C++里面的模板,但是有更强的型别,也更简单(更简单使用,功能稍逊)。
// Mark both Int and Double as being convertible to a Double using the '+' prefix protocol DoubleConvertible { @prefix func +(v: Self) -> Double } @prefix func +(v: Int) -> Double { return Double(v) } extension Double: DoubleConvertible {} extension Int: DoubleConvertible {} // NOTE: Repeat this for all Int*, UInt*, and the Float type // The traits of a generalized point protocol PointTraits { typealias T class var dimensions: Int { get } func getCoordinate(dimension: Int) -> T } // Generalized Pythagoras struct Pythagoras<P1: PointTraits, P2: PointTraits where P1.T: DoubleConvertible, P2.T: DoubleConvertible> { static func apply(a: P1, b: P2, dimensions: Int) -> Double { if dimensions == 0 { return 0 } let d: Double = +a.getCoordinate(dimensions-1) - +b.getCoordinate(dimensions-1) // NOTE: '+' to convert to Double return d * d + apply(a, b: b, dimensions: dimensions-1) } static func apply(a: P1, b: P2) -> Double { let dimensions = P1.dimensions assert(P2.dimensions == dimensions) return apply(a, b: b, dimensions: dimensions) } }; import func Foundation.sqrt // NOTE: You can import a typealias­, struct­, class­, enum­, protocol­, var­, or func only func distance<P1: PointTraits, P2: PointTraits where P1.T: DoubleConvertible, P2.T: DoubleConvertible>(a: P1, b: P2) -> Double { assert(P1.dimensions == P2.dimensions) return sqrt(Pythagoras.apply(a, b: b)); } // A generalized 2D point struct Point2D<Number> : PointTraits { static var dimensions: Int { return 2 } var x: Number, y: Number func getCoordinate(dimension: Int) -> Number { return dimension == 0 ? x : y } // NOTE: The typealias T is inferred } let a = Point2D(x: 1.0, y: 2.0) let b = Point2D(x: 5, y: 5) Pythagoras.apply(a, b: b) // NOTE: Methods require all argument names, except the first distance(a, b) // NOTE: Functions do not require argument names // UIColor extension UIColor : PointTraits { class var dimensions: Int { return 4 } func getCoordinate(dimension: Int) -> Double { var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 getRed(&red, green: &green, blue: &blue, alpha: &alpha) switch dimension { case 0: return red case 1: return green case 2: return blue default: return alpha } } } distance(UIColor.redColor(), UIColor.orangeColor())
以上代码是受Boost中的Design Rationale的启发.几何的C++库。Swift中的泛型功能不是那么强,但是却能使源码比C++中泛型更具阅读性。
Swift的泛型是通过类型参数化的。每个参数类型要求实现一个特定的协议或者继承自一个特定的基类。在申明参数类型后,有一个可选的"where"项目能用于给这个类型(或者是他们的内部类型,就如上面那个PointTraits协议中的别名'T')添加额外的需求。这个"where“也能要求两种类型是相等的。
苹果有更多更完整的Generics章节。
选定Swift
现在你已经准备好了去看各种各样的源码了,甚至可以自己写了 :-)
在把你自己留在一个新的大的Swift未知大陆之前,我有几个最终的建议:
- Apple推荐你使用Int作为所有Integer类型,即使你之前用无符号数的地方。“只有在你真的需要一个与平台原生大小想相同的无符号integer类型时再使用UInt类型.”
- 如果你想知道@private和al.在哪:好吧,他们还没完成呢,后续版本会加进来的。
- 如果你创建了一个module,你可以Cmd+单击你的module的名字来查看你的module的自动生成的Swift头。
- Swift的modules其实是命名空间,因此在任何东西前加像 CF, NS, UI等的前缀。当创建第三方库是就不是那么绝对必要了。
Enjoy using Swift!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
别转管理,十年程序员老鸟给新手的几条忠告
在2006年,我开始了编程工作。当意识到来到了十年这个重要的时间关口时,我觉得有必要回顾一下这十年间所犯下的错误,做一做经验总结,并且给正在这个职业上奋斗的人们提出我的一些忠告。开发行业变化得很快,我无法肯定在未来十年里这些建议一直有效,但我的话对你们是肯定有帮助的,不管是刚开始工作的新人还是老手。 要坚持使用一个开发平台,构架或语言 在过去十年间,我所犯的最大错误是:几乎每次我换工作时也转换了开发平台。 我的经历从Java企业开发开始,到嵌入式C编程,到用VB、C# 和 C++开发Windows桌面应用程序,然后用Objective-C写iOS应用程序,到用NDK编写Android库,最后到写Android应用。 这个职业经历听起来令人敬畏,但实际上雇主并不关心。雇主们并不会重点关注你拥有10年各式各样的开发经验,但如果他们看到你没有足够的Android应用开发经验,你就不可能获得一个 Android开发的工作。 转换平台会对你的简历产生巨大的负面影响。对于一个雇主来说,实际上他没办法通过简历来验证你是不是某一特定开发领域的专家,为你付出大量资金是否合理。 大多数公司都不关心你的过去...
- 下一篇
苹果推出新博客,宣传全新编程语言 Swift
苹果今天在官方开发者网站推出了全新博客,新博客是为了宣传全新 Swift 编程语言。Swift 在2014年 WWDC 发布会上正式公布,是 iOS 和 OS X 系统编程语言 Objective-C 的继任者。Swift 编程语言为开发者提供了全新、简洁和高效的工具,让开发应用更简单、更快捷。苹果工程师们可以在 Swift 博客上分享语言开发的幕后故事,还会有各种技巧等内容。 Swift 的首席开发者 Chris Lattner 在新编程语言发布后,在自己的博客上讲述了开发幕后的故事。目前,Swift 博客上还没有什么内容,只提到了 Swift 编程语言的兼容性。有趣的是,Swift 博客是苹果官方网站的第一个博客,这也代表了苹果对开发者和消费者的态度更开放。点击这里访问 Swift 博客。
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启