本文概述
苹果在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开发。
试用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 time
var 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)
}
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 = stringValue
doSomething(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 parts
switch 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 = 1000
func description() -> String
{
return "This bottle has \(volume) ml"
}
}
let b = Bottle()
print(b.description())
如你所见, 声明和实现位于同一文件中。 Swift不再使用头文件和实现文件。让我们在示例中添加标签:
class Bottle
{
var volume: Int = 1000
var label:String
func description() -> String
{
return "This bottle of \(label) has \(volume) ml"
}
}
编译器会抱怨, 因为label是非可选变量, 并且在实例化Bottle时将不保存任何值。我们需要添加一个初始化程序:
class Bottle
{
var volume: Int = 1000
var label:String
init(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:String
init(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:String
init (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 ml
var 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鸟将它们带到了一个新的高度。