从 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 πrr
        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&shy;, struct&shy;, class&shy;, enum&shy;, protocol&shy;, var&shy;, 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!

时间: 2024-07-28 22:11:08

从 Objective-C 到 Swift —— Swift 糖果 【已翻译100%】的相关文章

在终端中运行 Swift 程序 【已翻译100%】

在读完苹果开发者文档( Apple Developer Docs)后,我发现: "Xcode的调试器包括一个Swift语言的交互版本,叫做REPL(Read-Eval-Print-Loop)...可以通过Xcode的控制台或终端来访问" 不幸的是,它并没有告诉你怎样从终端访问.好在这也不是什么难事. 输入如下命令即可(无论你用哪个beta,换掉app的名字) alias swift="/Applications/Xcode6-Beta2.app/Contents/Develo

使用 Swift 构建一个 iOS 的邮件应用 【已翻译100%】

在前几个月内,我一直在做InboxKit的研究,它是关于Inbox平台的IOS SDK.Inbox为和邮件数据的交互提供高层API,使得你可以忽略IMAP,Exchange,MIME的解析以及thread探测(当然还有很多其他事情...),并使你致力于完成富有创意的APP的创作上.我们的目标很简单:尽可能地打造一个优雅的,跨提供商的邮件应用.毕竟,它很难. 在Objective-C中,InboxKit使得创建邮件体验变得很轻松,那么,Swift又如何呢?Swift在WWDC后已正式被IOS社区所

Swift 如何实现手势识别 【已翻译100%】

在这次IOS应用开发教程中,我们打算实现手势识别.正如你所知道的,IOS支持大量的手势操作,它们能提供了很好的应用控制和出色用户体验. 让我们开始吧! 首先需要在Xcode中创建一个新的Single View Application: 然后点击Next,弹出的窗口要求你填写项目设置.在第一栏 ("Product name") 中填入项目名称后,点击Next. 确保语言选择的是 "Swift". 设计界面 点击 "Main.storyboard"

Swift 在对 Objective-C 改进的 6 个方面 【已翻译100%】

在 Atomic Object 的安娜堡办公室,我们做了一个观看2014年的WWDC主题演讲,毫不夸张地说,当Swift宣布的时候,我们感到十分激动.Swift,苹果正在推进的一个更现代的编程语言.我很高兴能获得先机并开始用它开发. 在这篇博文中,我将重点介绍Swift的几个语言特性,这将使开发者的工作更加轻松. Objective-C语言的问题 当一个开发者申请一个关于Atomic Object的新职位时,我们会给他填写一个(GTKY) Getting To Know You的表.这个表要求填

从 C++ 到 Objective-C 的快速指南 【已翻译100%】

**简介 ** 当我开始为iOS写代码的时候,我意识到,作为一个C++开发者,我必须花费更多的时间来弄清楚Objective-C中怪异的东西.这就是一个帮助C++专家的快速指南,能够使他们快速的掌握Apple的iOS语言. 请注意这绝不是一个完整的指南,但是它让你避免了阅读100页的手册.除此之外,我知道你喜欢我的写作风格. 背景 需要C++的技能,我会比较C++和Objective-C的东西.此外,COM编程也是有用的,因为Objective-C有类似于IUnkown的东西,因此基础的COM编

Groovy 现在可运行在 Android 系统上了! 【已翻译100%】

在最近的GR8Conf Europe 2014期间,SpringSource/Pivotal的Groovy高级软件工程师 Cédric Champeau已经在Andriod上完成一次Groovy代码的现场pull请求的合并. Groovy开发人员为了在Android上运行Groovy已经等待好几年了.因为各种困难,它的实现被推迟了,这包括Andriod Dalvik虚拟机的不同字节码和Groovy代码的动态特性等.官方对Android的支持可能在Groovy2.4版本.InfoQ采访了 Cham

50 个 jQuery 插件可将你的网站带到另外一个高度 【已翻译100%】

Web领域一直在发生变化并且其边界在过去的每一天都在发生变化(甚至不能以小时为计),随着其边界的扩展取得了许多新发展.在这些进步之中,开发者的不断工作创造了更大和更好的脚本,这些脚本以插件方式带来更好的终端用户体验,它们比原来更轻量级,还有更强的处理能力. 关键是这些新发展起来的脚本和插件是能构建响应式Web的,而且还不会丧失它们原有的功能特性--除了更优秀和更轻巧(就文件大小而言)之外,它们还不会增加页面加载的时间. 通过浏览文档,掌握JQuery的语法是很容易的.它可以支持选择DOM元素,创

一个易用的 WPF 自动完成文本框 【已翻译100%】

介绍 这篇文章的目的是在社区中分享一些我上个月完成代码,让一个简单的文本框拥有自定义的自动完成过滤器.这个想法的灵感来自于GMail的搜索功能.在我的项目中,自定义的控件需要如下所有我需要的功能: 它是容易使用的,集成到项目中时,需要的代码要尽量的少. 它需要兼容WCF.我的想法是像GMail一样创建一个分层的应用,过滤功能需要在服务器端执行,然后将结果通过WCF通道传送. 它需要过滤自定义数据(来自于数据库或者自定义的列表)并可搜索多个字段,像GMail一样,建议类似的结果. 所有的过滤需要异

SQLite4 的设计 【已翻译100%】

1.0 内容提要 1.SQLite4 是一个放在库中的紧凑的,自包含的,零维护的的ACID数据库引擎, 像SQLite3一样, 但具有改进的接口和文件格式. 2.运行时环境封装到了一个对象之中. 3.使用了一个很不错的键值对存储引擎: 一个独立的大型键空间 - 不是SQLite3中那种每个表单独的键空间和索引. 2.按字典顺序的键排序. 3.多个存储引擎,可在运行时互换. 4.默认在磁盘上的存储殷勤使用了一个日志结构的合并数据库. 4.表的PRIMARY KEY真正被用作存储引擎的键. 5.可以