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

本文概述

苹果在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 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鸟将它们带到了一个新的高度。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?