美文网首页swift学习iOS Developer
Swift语言要点:Objective-C开发人员的观点【ray

Swift语言要点:Objective-C开发人员的观点【ray

作者: Fiona_L | 来源:发表于2016-11-21 15:10 被阅读64次

    如果这周一你和我一样,正坐下来享受keynote,兴奋的开始尝试所有全新的可爱的API。 然后你猛然听到谈论一种新语言时,你竖起了耳朵:Swift! 它深深震撼了你,因为这不是对Objective-C的扩展,而是一种完全全新的语言。 也许你很兴奋? 也许你很高兴? 也许你不知道该怎么想。
    Swift肯定改变了我们将来编写iOS和Mac程序的方式。 在这篇文章中,我概述了Swift语言的一些亮点,将它们与Objective-C中的对应部分进行了对比。
    注意这篇文章不是一个Swift入门指南。 苹果已经发布了一本很棒的书,我强烈建议阅读它。 相反,这里着重讨论swift中值得注意和玩的特别酷的地方!

    Swift-250x250.png

    类型

    Swift提供的第一个巨大利器是类型推断。在使用类型推断的语言中,程序员不需要用类型信息注释变量。编译器会根据赋值的变量去推断它。例如,编译器可以自动将此变量设置为String:

    //自动推断
    var name1 =“Matt”
    //显式类型(在这种情况下是可选的)
    var name2:String =“Matt”
    

    伴随类型推断,Swift还提供了类型安全。在Swift中,编译器(除了少数几种特殊情况)都知道对象的完整类型。因为掌握更多的信息,所以编译器可以决定关于如何编译代码。
    这与Objective-C形成鲜明对比,Objective-C本质上是非常动态的。在Objective-C中,编译时并不知道类型。这是因为你可以在运行时向现有类添加方法,添加完全新的类,甚至修改实例的类型。
    让我们更详细地看一看,考虑下面的Objective-C:

    Person * matt = [[Person alloc] initWithName:@“Matt Galloway”];
    [matt sayHello];
    ```
    当编译器看到调用sayHello时,它可以检查是否头部中声明了这个方法,它可以对Person上调用sayHello类型。如果没有该方法则会报错,但这是它只能做到这些。这通常足以捕捉引用的第一行的bug。它能捕获类似代码书写错误的bug。但是因为动态本质,编译器不知道sayHello是否会在运行时改变,甚至是否必须存在。比如,它可以是协议上的可选方法。 (可以用responsesToSelector:来检查)。
    由于缺乏强类型,在Objective-C中编译器在调用方法时几乎做不了什么做优化。处理动态分发的方法称为objc_msgSend。我相信你在许多回溯中都能看到这一点!在这个方法中,方法选择器的实现先查找然后跳转。这是一定会增加开销和复杂性的。
    现在看看Swift中的相同的代码:
    ```
    var matt = Person(name:“Matt Galloway”)
    matt.sayHello()
    ```
    在Swift中,编译器对所有方法调用中的类型都了解更多。它确切地知道sayHello()被定义的位置,因此可以通过直接跳转到实现替代动态分发来优化某些调用。其他情况下,它可以使用vtable样式分发,这样的开销远远小于Objective-C中的动态分发。这类似于C ++中的虚拟函数调用。
    编译器在Swift中更能发作用。它能帮助阻止子类型相关的bug进入你的代码库,还能通过智能优化使代码运行更快。
    ###泛型
    Swift的另一个巨大的特性是泛型。如果你熟悉C++,那么你可以认为这个就像模板。因为Swift对类型很严格,所以你必须声明一个方法能够接受某些类型的参数,但有时有些同一种方法应当兼容多个不同类型。
    这种情况的一个例子是,您希望将一对值存储在一起,因为他们是经常被使用的结构体。对于蒸熟你可以在Swift中这样实现:
    ```
    struct IntPair {
        让a:Int!
        让b:Int!
        init(a:Int,b:Int){
            self.a = a
            self.b = b
        }}
        func equal() - > Bool {
            return a == b
        }}
    }}
    let intPair = IntPair(a:5,b:10)
    intPair.a // 5
    intPair.b // 10
    intPair.equal()// false
    ```
    搞定!但现在你想要这个方法也适用于浮点数。你可以定义一个FloatPair类,但是她们非常相似。这时候泛型就该发挥作用了。不需要声明一个全新的类,你可以这样做:
    ```
    struct Pair <T:Equatable> {
        让a:T!
        让b:T!
        init(a:T,b:T){
            self.a = a
            self.b = b
        }}
        func equal() - > Bool {
            return a == b
        }}
    }}
    let pair = Pair(a:5,b:10)
    pair.a // 5
    pair.b // 10
    pair.equal()// false
    let floatPair = Pair(a:3.14159,b:2.0)
    floatPair.a // 3.14159
    floatPair.b // 2.0
    floatPair.equal()// false
    ```
    很棒!现在你可能还不太清楚为什么你需要这个特性,但相信我:用到机会是数不清的。你很快就会看到自己在代码中用到这些。
    ###容器
    你已经知道并喜欢NSArray,NSDictionary和他们的可变类型。好了,现在你将要了解它们在Swift中的对应物,幸运的是他们很相似。下面是如何声明数组和字典:
    ```
    let array = [1,2,3,4]
    let dictionary = [“dog”:1,“elephant”:2]
    ```
    尽管有细微的差别,但你还是应该很熟悉。在Objective-C中,只要你愿意,数组和字典可以同时包含任何类型。但在Swift中,数组和字典都是被指定类型,而且是通过泛型来指定类型!
    上面的两个变量可以像下面这样重写它们的类型表达(即使实际上你不必这样做!):
    ```
    let array:Array <Int> = [1,2,3,4]
    let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]
    ```
    注意如何使用泛型定义存储在容器中的内容。它还有一个更简短,可读性更强的形式,但本质相同:
    ```
    let array:[Int] = [1,2,3,4]
    let dictionary:[String:Int] = [“dog”:1,“elephant”:2]
    ```
    注意,您不能把非Int类型的内容添加到数组中。这听起来不怎么友好,但其实非常有用。API不再需要记录什么被存储在数组中,它由某个方法返回或存储在属性中。编译器可以立即获取这些信息,以便它可以更好地检查错误和优化。
    ###可变类型
    Swift中集合的一个有趣之处在于它们的可变类型。数组和字典没有“可变”对应,而是使用标准let和var。对于那些还没有读过这本书,或者深入到Swift中的人(我建议你尽快去读!),let用于声明常量,var用于声明变量,变量! let就像在C / C ++ / Objective-C中使用const。
    与集合相关的方式是使用let声明的集合不能更改大小。也就是说,它们不能添加到或移除元素。如果你试图这么做,那么你会收到报错,如:
    ```
    let array = [1,2,3]
    array.append(4)
    // error: immutable value of type 'Array<Int>' only has mutating members named 'append'
    ```
    这同样适用于dictionary。这就允许编译器推断集合,并进行适当的优化。例如,大小不能改变,则存储这些值的存储器不需要为了适应新值被重新分配空间。因此,申请一个不可变的集合时应当使用let。
    ###字符串
    Objective-C中对字符串的处理真是臭名昭着。即使简单的任务,如连接许多不同的值都非常乏味。请参见以下示例:
    ```
    Person *person = ...;
    NSMutableString * description = [[NSMutableString alloc] init];
    [description appendFormat:@“%@ is%i years old.”,person.name,person.age];
    if(person.employer){
      [description appendFormat:@“They work for%@.”,person.employer];
    } else {
      [description appendString:@" They are unemployed."];
    }
    ```
    这是相当冗长乏味的,并且包含了很多与被操作的数据无关的字符。在Swift中将会是这样:
    ```
    var description =“”
    description + =“\(person.name)is \(person.age)years old。”
    if person.employer { 
       description += " They work for \(person.employer)."
    } else { 
       description += " They are unemployed."
    }
    ```
    非常清晰!注意这个按格式创建字符串的更简洁的方法,你现在可以通过使用+=连接字符串,不再有可变字符串和不可变字符串。
    Swift中另一个很棒的地方是字符串的比较。在Objective-C中使用==比较字符串的相等性是不对的,而是应该用isEqualToString:方法。这是因为前者用于比较指针相等。 Swift删除这个中间的级别,能够直接使用==来比较字符串。这也意味着字符串可以在switch语句中使用。相关的更多问题在下一节讨论。
    最后一个好消息是,Swift本身支持完整的Unicode字符集。你可以在字符串中使用任何Unicode代码点,甚至方法和变量名!只要你愿意甚至可以有一个方法叫💩(一坨屎!)!
    另一个好消息是,现在有一个内置的方法来计算字符串的真实长度。当涉及到完整的Unicode范围时,字符串长度的计算是不重要的。因为有的字符超过1个字节,所以你不能只是说它是在UTF8中存储字符串的字节数。在Objective-C中,使用2字节对对来存储字符串,所以NSString通过计算UTF16的数量进行计算,但这在技术上不是正确的,因为一些Unicode代码点占用两个2字节对。
    幸运的是,Swift有一个方便的方法来计算字符串中代码点的真实数量,是叫做countElements()的上层方法。你可以这样用:
    ```
    var poos =“\ u {1f4a9} \ u {1f4a9}”
    countElements(poos)// 2
    ```
    但它仍然不适用于所有情况,它只是计算Unicode代码点的数量,不考虑改变其他字符的特殊代码点。例如,可以在先前的字符上放一个变音符号。这种情况下,countElements()将返回2,即使它看起来像只有1个字符。像这样:
    ```
    var eUmlaut =“e \ u {0}”
    countElements(eUmlaut)// 2
    ```
    通过以上这些,相信你会同意Swift中的字符串是棒的!
    ###Switch语句
    在Swift的简短介绍中,我想要提到的最后一件事是switch语句。相比较于Objective-C中的这部分,Swift中已经有了显着的改进。这非常有趣,因为基于Objective-C是C的严格超集,这些改变不可能加到Objective-C中。
    第一个令人兴奋的功能是关于字符串的Switch。这可能是你之前想做却不能做的事情,要在Objective-C中“switch”字符串,你必须使用大量带有isEqualToString的if语句,就像这样:
    ```
    if ([person.name isEqualToString:@"Matt Galloway"]) { 
       NSLog(@"Author of an interesting Swift article");
    } else if ([person.name isEqualToString:@"Ray Wenderlich"]) { 
       NSLog(@"Has a great website");
    } else if ([person.name isEqualToString:@"Tim Cook"]) {
       NSLog(@"CEO of Apple Inc.");
    } else {
       NSLog(@"Someone else);
    }
    ```
    这要打很多字,可读性也不高。然而Swift中是这样:
    ```
    switch person.name { 
         case "Matt Galloway": 
             println("Author of an interesting Swift article") 
         case "Ray Wenderlich":
             println("Has a great website") 
         case "Tim Cook": 
             println("CEO of Apple Inc.") 
         default: 
             println("Someone else")
    }
    ```
    除了关于字符串的Switch,注意这里还有一些有趣的地方,没有break。这是因为Switch中的case不会再执行下一个,不会出现意外出错的情况!
    准备好,下面的switch语句很可能会打击你!
    ```
    switch i {
    case 0, 1, 2:
            println("Small")
    case 3...7:
            println("Medium")
    case 8..<10:
            println("Large")
    case _ where i % 2 == 0:
            println("Even")
    case _ where i % 2 == 1:
            println("Odd")
    default:
            break
    }
    ```
    这里出现了break。因为Switch需要是穷尽的,即需要能够处理所有情况。这种情况下,希望default不做任何事情,所以添加了一个断点来声明不应该发生任何事情。
    下一个有趣的事情是你看到的...和.. <。这些是新的运算符,用于定义范围。前者,定义了直到并包括右边界的范围。后者定义了一个范围,直到不包括右边界。这些都非常有用。
    最后一件事是定义case作为输入的计算。这种情况下,如果值不匹配从零到十的任何值,则打印“Even”(如果是偶数)和“Odd”(如果它是奇数)。神奇!
    ###未来发展
    这里只是希望给你体验一下Swift语言和它的奇妙之处。 但还有更多! 阅读苹果的书和[其他](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)苹果[文档](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26),将帮助你学习这种新的语言。 你迟早要这么做的!
    此外,如果你想了解更多关于Swift,请查看我们[三个Swift的新书](https://www.raywenderlich.com/?page_id=74805) - 涵盖你需要知道的关于Swift,iOS 8等等!
    我们很想听听你对Swift语言的看法,或者如果你有令人兴奋的亮点。 请联系我!
    
    [原文链接](https://www.raywenderlich.com/73997/swift-language-highlights) 本文为个人翻译,不用与任何商业用途,如需转载请与本人联系。

    相关文章

      网友评论

        本文标题:Swift语言要点:Objective-C开发人员的观点【ray

        本文链接:https://www.haomeiwen.com/subject/dozdpttx.html