iOS面试剖析

Swift 与 Objective-C 的10个区别

2019-09-25  本文已影响0人  zhongxiaoyue

简介

2014年6月2日,Apple推出了一种全新的面向对象的编程语言:Swift,它是Objective-C的替代产品,也是OS X和iOS应用程序开发的标准编程语言。


Swift 与 Objective-C 的10个区别


1.可选值

可选值是C或Objective-C中不存在的概念。它们允许可能并不总是能够返回有意义的值的函数(例如,在输入无效的情况下)返回封装在optional或nil中的值。在C和Objective-C中,我们已经可以从通常会返回对象的函数中返回nil,但是对于期望返回基本类型(例如int,float或double)的函数,我们没有此选项。注意:

    let a = "512"
    let b = Int(a)
    print(b) // Optional(512)

在这种情况下,Int函数按预期返回包含int值512的可选。但是,如果我们尝试转换“ Hello”之类的东西怎么办?在这种情况下,该函数将返回nil,如上所述:

    let a = "Hello"
    let b = Int(a)
    print(b) // nil

您可能已经知道,在Objective-C中,如果在nil指针上调用方法,则不会发生任何事情。没有编译错误,没有运行时错误,什么都没有。这是众所周知的错误,意外行为和崩溃的根源:缺乏反馈意味着开发人员被迫独自追查这些错误的根源。
使用Swift,您不必担心:

    var x = Int("555")
    print(x.successor()) // error

编译器本身将引发错误,因为x现在是可选的,并且可能为nil。在使用可选值之前,您需要像这样解开包装:

   var x = Int("555")
   if x != nil {
       print(x!.successor()) // 556
   }

或者,我们可以使用可选绑定:

    var x = Int("555")
    if var y = x {
        y += 445
        print(y) // 1000
    }

2.控制流

用C或类似C的语言进行编程的任何人都熟悉使用花括号{}来分隔代码块。但是,在Swift中,它们不仅仅是一个好习惯而是它们是规定!

    if a < 5 {
        print("Something")
    }

这在Swift中会引发编译器错误!

    if a < 5
    print(“Something")

与Objective-C不同,Swift将大括号视为ifforwhilerepeat语句的必需项。这听起来可能不是对Objective-C本身的改进,但请考虑,即使是苹果自己的开发人员,如果不使用大括号来保持一致,也会遇到麻烦:

    if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;

没错:如果Objective-C像Swift一样严格地执行花括号,就可以避免臭名昭著的iOS"goto fail"错误!


3.类型推断

Swift将类型安全性引入了iOS开发。一旦使用特定类型声明了变量,则其类型为静态,无法更改。编译器也足够聪明,可以根据分配给它们的值来确定(或推断)变量应该是哪种类型:

    var str = "Some string"
    // OR
    var str2:String
    str2 = "Other string"

这也意味着如果您尝试为我们的str2变量分配一个数字值(例如10),则编译器将引发错误:

    str2 = 10 // error: Cannot assign value of type 'Int' to type 'String'

比较Objective-C,您必须始终明确声明变量的类型:

    NSString str = @"There is no type inference in Objective-C :("

4.元组

Swift支持元组(存储其他值组的值)。与数组不同,元组中的值不必都是同一类型。例如,您可以具有字符串和整数的元组:

    var t:(String, Int) = ("John", 33)
    print(t.0, t.1)
 
    var j:(name:String, Int) = ("Morgan", 52)
    print(j.name, j.1)

元组最明显的用途是从一个函数返回多个值:

    var arr = [23, 5, 7, 33, 9]
 
    func findPosition(el:Int, arr:[Int]) -> (found:Bool, position:Int)? {
        if arr.isEmpty {
            return nil
        }
 
        for (i, value) in arr.enumerate() {
            if value == el {
                return (true, i)
            }
        }
 
        return (false, -1)
    }
 
    if let (isFound, elPosition) = findPosition(5, arr: arr) {
        print(isFound, elPosition)
        // true 1
    }

在Objective-C中,我们可以以类似的方式使用block,但它并不那么直接或优雅。


5.字符串操作

Swift在字符串操作方面比Objective-C有了巨大的改进。对于初学者来说,您不必再担心可变的字符串和不可变的字符串:如果要在将来更改它,只需用var声明您的字符串,或者如果需要使其保持不变,则可以使用let声明
字符串连接就像1 + 1 = 2一样简单:

    // Swift:
    var str = "My string"
    str += " and another string”

比较Objective-C,其中连接两个不可变字符串需要您创建一个全新的NSString对象。这是使用“ stringWithFormat”方法的示例:

    // Obj-C:
    NSString *myString = @"My string";
    myString = [NSString stringWithFormat:@"%@ and another string", myString];

如您所见,Objective-C中的字符串格式化涉及为要插入到字符串中的每种不同数据类型插入特殊的占位符:

    NSString *str = [NSString stringWithFormat:@"String: %@ | Signed 32-bit integer: %d | 64-bit floating-point number: %f", @"My String", myInt, myFloat];

在Swift中,我们可以通过将变量名称直接插入到字符串中来插值:

    var str = "String: \(myString) | Signed 32-bit integer: \(myInt) | 64-bit floating-point number: \(myFloat)"

6.Guard & Defer

听说过“厄运金字塔”吗?如果没听过,那么这是一段Objective-C代码,这段代码可以唤醒你的记忆:

    enum TriangleAreaCalcError: ErrorType {
        case AngleNotSpecified
        case InvalidAngle
        case SideANotSpecified
        case SideBNotSpecified
    }
 
    func calcTriangleArea(a: Double ? , b : Double ? , alpha : Double ? ) throws - > Double {
        if let a = a {
            if let b = b {
                if let alpha = alpha {
                    if alpha < 180 && alpha >= 0 {
                        if alpha == 180 {
                            return 0
                        }
                        return 0.5 * a * b * sin(alpha * M_PI / 180.0)
                    } else {
                        throw TriangleAreaCalcError.InvalidAngle
                    }
                } else {
                    throw TriangleAreaCalcError.AngleNotSpecified
                }
            } else {
                throw TriangleAreaCalcError.SideBNotSpecified
            }
        } else {
            throw TriangleAreaCalcError.SideANotSpecified
        }
    }

幸运的是,在Swift中,我们有了guard,这是一个新的条件语句,可使该代码更具可读性。如果不满足条件,guard将停止程序流:

    func calcTriangleArea(a: Double ? , b : Double ? , alpha : Double ? ) throws - > Double {
 
        guard
        let a = a
        else {
            throw TriangleAreaCalcError.SideANotSpecified
        }
 
        guard
        let b = b
        else {
            throw TriangleAreaCalcError.SideBNotSpecified
        }
 
        guard
        let alpha = alpha
        else {
            throw TriangleAreaCalcError.AngleNotSpecified
        }
 
        if alpha == 180 {
            return Double(0)
        }
 
        guard alpha < 180 && alpha >= 0
        else {
            throw TriangleAreaCalcError.InvalidAngle
        }
 
        return 0.5 * a * b * sin(alpha * M_PI / 180.0)
    }

Swift还为我们提供了defer关键字,该关键字提供了一种安全简便的方法来处理仅在程序离开当前作用域时才希望执行的代码:

    func someImageFunc() - > UIImage ? {
 
        let dataSize: Int = ...
        let destData = UnsafeMutablePointer < UInt8 > .alloc(dataSize)
 
        // ...
 
        guard error
        else {
            destData.dealloc(dataSize) // #1
            return nil
        }
 
        guard error2
        else {
            destData.dealloc(dataSize) // #2
            return nil
        }
 
        guard error3
        else {
            destData.dealloc(dataSize) // #3
            return nil
        }
 
        destData.dealloc(dataSize) // #4
        
        // ...
    }

如您所见,我们需要对destData.dealloc(dataSize)编写四个单独的调用,以确保即使在发生错误的情况下使函数退出,我们的指针也会被释放。使用defer,我们可以轻松地清理指针和代码:

    func someImageFunc() - > UIImage ? {
 
        let dataSize: Int = ...
            let destData = UnsafeMutablePointer < UInt8 > .alloc(dataSize)
 
        // ...
 
        defer {
            destData.dealloc(dataSize)
        }
 
        guard error
        else {
            return nil
        }
 
        guard error2
        else {
            return nil
        }
 
        guard error3
        else {
            return nil
        }
 
        // ...
    }

7.函数式编程模式

Swift结合了许多函数式的编程功能,例如mapfilter,可在实现CollectionType协议的任何集合上使用:

    let a = [4, 8, 16]
    print(a.map{$0 / 2})
    // [2, 4, 8]
 
    let a:[(name:String, area:String)] = [("John", "iOS"), ("Sam", "Android"), ("Paul", "Web")]
 
    let b = a.map({"Developer \($0.name) (\($0.area))"})
    // ["Developer John (iOS)", "Developer Sam (Android)", "Developer Paul (Web)”]
 
    let a = [23, 5, 7, 12, 10]
    let b = a.filter{$0 > 10}
    print(b) // [23, 12]
 
    let sum = (20...30)
    .filter { $0 % 2 != 0 }
    .map { $0 * 2 }
    .reduce(0) { $0 + $1 }
 
    print(sum) // 250

Objective-C没有对函数式编程的内置支持。要使用这些相同的功能,您必须使用第三方库。


8.枚举

在Swift中,枚举比在Objective-C中更强大:它们现在可以包含方法并可以通过值进行传递。这是一小段代码,很好地说明了枚举在Swift中的工作方式:

    enum Location {
        case Address(city: String, street: String)
        case Coordinates(lat: Float, lon: Float)
 
        func printOut() {
            switch self {
                case let.Address(city, street):
                    print("Address: " + street + ", " + city)
                case let.Coordinates(lat, lon):
                    print("Coordiantes: (\(lat), \(lon))")
            }
        }
    }
 
    let loc1 = Location.Address(city: "Boston", street: "33 Court St")
    let loc2 = Location.Coordinates(lat: 42.3586, lon: -71.0590)
 
    loc1.printOut() // Address: 33 Court St, Boston
    loc2.printOut() // Coordiantes: (42.3586, -71.059)

枚举也可以是递归的,因此我们可以使用indirect语句构建一个链表,它告诉编译器添加必要的间接层:

    enum List {
        case Empty
        indirect
        case Cell(value: Int, next: List)
    }
 
    let list0 = List.Cell(value: 1, next: List.Empty)
    let list1 = List.Cell(value: 4, next: list0)
    let list2 = List.Cell(value: 2, next: list1)
    let list3 = List.Cell(value: 6, next: list2)
    let headL = List.Cell(value: 3, next: list3)
 
    func evaluateList(list: List) - > Int {
        switch list {
            case let.Cell(value, next):
                return value + evaluateList(next)
            case .Empty:
                return 0
        }
    }
 
    print(evaluateList(headL)) // 16

9.函数

Swift的函数语法非常灵活,可以定义任何东西,从简单的C样式函数到具有本地和外部参数名称的复杂的Objective-C样式方法。

Swift中的每个函数都有一个类型,该类型由函数的参数类型和返回类型组成。这意味着您可以将函数分配给变量或将它们作为参数传递给其他函数:

    func stringCharactersCount(s: String) - > Int {
        return s.characters.count
    }
 
    func stringToInt(s: String) - > Int {
        if let x = Int(s) {
            return x
        }
        return 0
    }
 
    func executeSuccessor(f: String - > Int, s: String) - > Int {
        return f(s).successor()
    }
 
    let f1 = stringCharactersCount
    let f2 = stringToInt
 
    executeSuccessor(f1, s: "5555") // 5
    executeSuccessor(f2, s: "5555") // 5556

Swift还允许您定义函数参数的默认值:

    func myFunction(someInt: Int = 5) {
        // If no arguments are passed, the value of someInt is 5
    }
    myFunction(6) // someInt is 6
    myFunction() // someInt is 5

10.Do语句

Swift中的do语句使您可以引入新的作用域:

    let a = "Yes"
 
    do {
        let a = "No"
        print(a) // No
    }
 
    print(a) // Yes

Do语句还可以包含一个或多个catch子句:

    do {
        try expression
        statements
    } catch pattern 1 {
        statements
    } catch pattern 2 where condition {
        statements
    }

有关更多信息,请参阅官方的Swift文档中的do语句。

上一篇下一篇

猜你喜欢

热点阅读