iOS开发人员指南(从Objective-C到学习Swift)

本文概述

  • 试用Swift与Objective-C
  • 变量和常量
  • 可选
  • 字符串插值
  • 函数
  • 枚举
  • 元组
  • 类和结构
  • 未完待续
苹果在2008年宣布并发布了iPhone SDK 2.0。此事件引发了软件开发的另一场革命, 并且诞生了新一代的开发人员。他们现在被公认为iOS开发人员。
这些开发人员中有许多以前从未使用过Objective-C, 这是Apple向他们提出的第一个挑战。尽管语法和手动内存管理不熟悉, 但它还是非常成功的, 它为App Store填充了成千上万的应用程序。苹果公司在每个版本中都不断改进Objective-C, 增加了块和文字, 通过自动引用计数简化了内存管理, 以及许多其他指示现代编程语言的功能。
经过6年的改进并致力于Objective-C, 苹果公司决定向开发人员提出另一个挑战。同样, iOS开发人员将需要学习一种新的编程语言:Swift。 Swift删除了不安全的指针管理并引入了强大的新功能, 同时保持了与Objective-C和C的交互。
Swift 1.0已经是一个稳定而强大的开发平台, 并且肯定会在未来几年内以有趣的方式发展。现在正是探索这种新语言的绝佳时机, 因为这显然是iOS开发的未来。
本教程的目的是为Objective-C开发人员提供有关Swift新语言功能的快速概述, 帮助你迈出下一步, 并开始在日常工作中采用Swift。我不会花太多时间来解释Objective-C, 并且我假设你熟悉iOS开发。
iOS开发人员指南(从Objective-C到学习Swift)

文章图片
试用Swift与Objective-C 为了开始探索Swift, 你需要做的就是从App Store下载XCode并创建一个游乐场进行实验。本文提到的所有示例都是通过这种方式完成的。
Apple的Swift主页是学习Swift编程的最佳参考。你会发现它非常宝贵, 在你完全掌握Swift开发之前, 我相信你会经常回到这里。
变量和常量 在Swift中声明变量是使用var关键字完成的。
var x = 1 var s = "Hello"

你会注意到两个变量x和s是不同类型的。 x是一个整数, 而s是一个字符串。 Swift是一种类型安全的语言, 它将从分配的值中推断出变量类型。如果希望使代码更具可读性, 则可以选择注释变量的类型:
var y: Int y = 2

常量相似, 但是你可以使用let而不是var来声明它们。常量的值不需要在编译时就知道, 但是你必须为它赋值一次。
let c1 = 1 // Constant known at compile timevar v = arc4random() let c2 = v // Constant known only at run time

顾名思义, 它们是不可变的, 因此以下代码将导致编译时错误。
let c = 1 c = 3// error

其他类型也可以声明为常量。例如, 以下代码将数组声明为常量, 并且如果你尝试修改任何元素, 则Swift编译器将报告错误:
var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2)// [8, 5, 6]let arr = [1, 2, 3] a[0] = 5// error

可选 声明常量时需要对其进行初始化, 而变量在使用前需要进行初始化。那么, Objective-C nil在哪里呢? Swift引入了可选值。可选值可以有一个值或为nil。如果看下面的代码, 你会注意到x被分配了一个Optional值2014。这意味着Swift编译器知道x也可能为nil。
var s = "2014" var x = s.toInt() print(x)// Optional(2014)

如果你对此代码进行更改并将值” abc” 分配给s, 而无法将其转换为Integer, 则会注意到x现在是anil。
var s = "abc" var x = s.toInt() print(x)// nil

toInt()函数的返回类型为Int ?, 它是可选的Int。让我们尝试在x上调用标准函数:
var x = "2014".toInt() print(x.successor()) // error

编译器发出错误信号, 因为x是可选的, 并且可能为nil。我们必须先测试x, 并确保后继函数是在实数而不是nil值上调用的:
var x = "2014".toInt() if x != nil { print(x!.successor())// 2015 }

请注意, 我们必须通过添加感叹号(!)来展开x。当我们确定x包含一个值时, 我们可以访问它。否则, 我们将收到运行时错误。我们还可以执行Swift所谓的可选绑定, 将可选变量转换为非可选变量
let x = "123".toInt() if let y = x { print(y) }

【iOS开发人员指南(从Objective-C到学习Swift)】if语句中的代码仅在x具有值并将其分配给y时执行。请注意, 我们不需要解包y, 它的类型不是可选的, 因为我们知道x不是nil。
查看Apple的Swift教程, 以了解有关可选选项和可选链接等出色功能的更多详细信息
字符串插值 在Objective-C中, 格式化字符串通常使用stringWithFormat:方法完成:
NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];

Swift具有称为字符串插值的功能, 可以做到这一点, 但它更紧??凑, 更易于阅读:
let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"

你还可以使用表达式:
let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"

要了解有关Swift的字符串插值和其他新功能的更多信息, 请转到此处。
函数 Swift中的函数定义与C不同。示例函数定义如下:
func someFunction(s:String, i: Int) -> Bool { ...// code }

Swift函数是一流的类型。这意味着你可以将函数分配给变量, 将它们作为参数传递给其他函数, 或使其返回类型:
func stringLength(s:String) -> Int { return countElements(s) }func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 }func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() }let f1 = stringLength let f2 = stringValuedoSomething(f1, "123")// 4 doSomething(f2, "123")// 124

同样, Swift可以推断f1和f2的类型(字符串-> Int), 尽管我们可以明确定义它们:
let f1:String -> Int = stringLength

函数还可以返回其他函数:
func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b }func compareLessThan(a: Int, b: Int) -> Bool { return a < b }func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } }let f = comparator(true) println(f(5, 9))

你可以在此处找到Swift的功能指南。
枚举 Swift中的枚举比Objective-C中的强大得多。作为Swift结构, 它们可以具有方法, 并按值传递:
enum MobileDevice : String { case iPhone = "iPhone", Android = "Android", WP8 = "Windows Phone8", BB = "BlackBerry"func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name())// "Android"

与Objective-C不同, Swift枚举可以为每个成员分配字符串, 字符或浮点数作为值, 除了整数。方便的toRaw()方法返回分配给每个成员的值。
枚举也可以参数化:
enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float)func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } }let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description())// "2070 Fell St, San Francisco" print(loc2.description())// "(23.117, 45.988)"

有关枚举的更多信息, 请参见此处。
元组 元组将多个值分组为一个复合值。元组中的值可以是任何类型, 而不必彼此相同。
let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel

你还可以命名单个元组元素:
let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)

元组作为需要返回多个值的函数的返回类型非常方便:
func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3))// (3, 2) let result = intDivision(15, 4) print(result.remainder)// 3

与Objective-C不同, Swift在switch语句中支持模式匹配:
let complex = (2.0, 1.1)// real and imaginary partsswitch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }

在第二种情况下, 我们不在乎数字的实数部分, 因此我们使用_来匹配任何内容。你还可以在每种情况下检查其他条件。为此, 我们需要将模式值绑定到常量:
let complex = (2.0, 1.1)switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }

注意, 我们只需要绑定将要在比较或case语句中使用的值。
要了解有关元组的更多信息, 请转到此处。
类和结构 与Objective-C不同, Swift不需要你为自定义类和结构创建单独的接口和实现文件。学习Swift时, 你将学习在单个文件中定义类或结构, 并且该类或结构的外部接口会自动提供给其他代码使用。
定义班级
类定义非常简单:
class Bottle { var volume: Int = 1000func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())

如你所见, 声明和实现位于同一文件中。 Swift不再使用头文件和实现文件。让我们在示例中添加标签:
class Bottle { var volume: Int = 1000 var label:Stringfunc description() -> String { return "This bottle of \(label) has \(volume) ml" } }

编译器会抱怨, 因为label是非可选变量, 并且在实例化Bottle时将不保存任何值。我们需要添加一个初始化程序:
class Bottle { var volume: Int = 1000 var label:Stringinit(label:String) { self.label = label }func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

或者, 我们可以对属性使用Optional类型, 该属性不进行初始化。在以下示例中, 我们将volume设置为Optional Integer:
class Bottle { var volume: Int? var label:Stringinit(label:String) { self.label = label }func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }

结构体
Swift语言也具有结构, 但是它们比Objective-C灵活得多。以下代码教程定义了一个结构:
struct Seat { var row: Int var letter:Stringinit (row: Int, letter:String) { self.row = row self.letter = letter }func description() -> String { return "\(row)-\(letter)" } }

像Swift中的类一样, 结构可以具有方法, 属性, 初始化器并符合协议。类和结构之间的主要区别在于, 类是通过引用传递的, 而结构是通过值传递的。
此示例通过引用演示了传递类:
let b = Bottle() print(b.description())// "b" bottle has 1000 mlvar b2 = b b.volume = 750 print(b2.description())// "b" and "b2" bottles have 750 ml

如果我们对struct尝试类似的情况, 你会注意到变量是通过值传递的:
var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description())// 14-B print(s2.description())// 14-A

什么时候应该使用struct?什么时候应该使用class?与在Objective-C和C中一样, 当你需要对一些值进行分组并希望将其复制而不是引用时, 请使用结构。例如, 复数, 2D或3D点或RGB颜色。
传统上将类的实例称为对象。但是, Swift类和结构在功能上比其他语言要紧密得多, 许多功能可以应用于类或结构类型的实例。因此, 在Swift引用中使用的更通用的术语是实例, 它适用于这两个实例中的任何一个。
在这里学习Swift类和结构的基础知识。
属性
如前所述, Swift中的属性在类或结构定义中使用var关键字声明。我们还可以使用let语句声明常量。
struct FixedPointNumber { var digits: Int let decimals: Int }var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567// ok n.decimals = 3// error, decimals is a constant

还要记住, 除非你为类属性添加前缀weak关键字, 否则强烈建议使用类属性。但是, 有些细微之处还具有较弱的非可选属性, 因此请阅读Apple的Swift指南中的自动引用计数章节。
计算属性
计算属性实际上并不存储值。相反, 它们提供了一个getter和一个可选的setter, 以间接检索和设置其他属性和值。
以下代码提供了计算值符号的示例:
enum Sign { case Positive case Negative }struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } }set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }

我们还可以通过仅实现getter来定义只读属性:
struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }

在Objective-C中, 属性通常由实例变量支持, 该实例变量可以显式声明或由编译器自动创建。另一方面, 在Swift中, 属性没有相应的实例变量。即, 不能直接访问属性的后备存储。假设我们在Objective-C中有这个
// .h @interface OnlyInitialString : NSObject@property(strong) NSString *string; @end// .m@implementation OnlyInitialString- (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } }@end

由于在Swift中, 计算属性没有后备存储, 因此我们需要执行以下操作:
class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }

属性在这里更详细地说明
未完待续 在Swift中, 还有许多重要的新知识需要学习, 例如泛型, 与Objective-C库的交互, 闭包, 可选链以及运算符重载。单个教程无法彻底描述一种新语言, 但是毫无疑问, 我将为学习Swift编程撰写更多的文章。但是, 我相信, 这种快速阅读将有助于许多Objective-C开发人员, 他们没有找到时间并学习Swift语言的细节, 走上正轨, 让Swift鸟将它们带到了一个新的高度。

    推荐阅读