澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

主导语法,秒钟入门教程

2019-06-29 作者:www.8455.com   |   浏览(52)

Swift是苹果推出的一个比较新的语言,它除了借鉴语言如C#、Java等内容外,好像还采用了很多JavaScript脚本里面的一些脚本语法,用起来感觉非常棒,作为一个使用C#多年的技术控,对这种比较超前的语言非常感兴趣,之前也在学习ES6语法的时候学习了阮一峰的《ECMAScript 6 入门》,对JavaScript脚本的ES6语法写法叹为观止,这种Swift语言也具有很多这种脚本语法的特点,可以说这个Swift在吸收了Object C的优点并摒弃一些不好的东西外,同时吸收了大量新一代语言的各种特点,包括泛型、元祖等特点。我在学习Swift的时候,发现官方的语言介绍文章(The Swift Programming Language)还是非常浅显易懂,虽然是英文,不过代码及分析说明都很到位,就是内容显得比较多一些,而我们作为技术人员,一般看代码就很好了解了各种语法特点了,基于这个原因,我对官网的案例代码进行了一个摘要总结,以代码的方式进行Swift语言的语法特点介绍,总结一句话就是:快看Sample代码,速学Swift语言。

1、Swift 2.0 带来哪些新变化

  • 常规变化:

    • 1、OS X 10.11、iOS 9 和 watchOS 2 SDK 采纳了一些 Objective-C 的特性用来提高 Swift 的编程体验, 如可空性、类型化集合和一些别的特性。

    • 2、编译器对冗余的协议一致性,未被使用的绑定值以及可以设为常量的变量这些情况目前会给予警告或报错。

    • 3、修复了跨文件协议遵循时符号不可见或者重复的错误。

    • 4、Swift 语言的调用约定更加智能,能够理解 API 所发生的变化和 Swift 所给出的警告。

    • 5、便利的可失败构造器(failable initializer)可以先返回 nil,而不必首先调用 self.init。这是有利的一 面,但指定了构造器在返回 nil 前仍要给所有字段初始化。

    • 6、find 函数改名为 indexOfsort 则变成了 sortInPlacesorted 变成了 sort

    • 7、String.toInt() 重名为 Int(String) 的可失败构造器,因为构造器语法更适合类型转换。

      • String 类型不再遵循 SequenceType,可以使用 .characters.utf8.utf16 对应字符集的运算。允许对泛型添加公共扩展。

      • 字符串长度长度计算由 count(String) 变为 String.characters.count

      • 字符串裁剪由 code.substringToIndex(advance(code.startIndex, 6)) 变为 let endIndex = code.startIndex.advancedBy(6) code.substringToIndex(endIndex)

    • 8、标准库中重构了很多泛型的全局函数(如 mapfiltersort),采用协议扩展方式增加这些方法。这个好处是对于其他的关联类型能很好的适配。

      • 非泛型类类型可以继承泛型类(强制类型参数固定)。

      • 修复了 Swift 中泛型需求打印时 “T==T” 的错误。

      • 在泛型函数中声明了类型参数但是在函数中没有使用时将产生一个编译时错误,例如:

      • func foo<T> () {} // error:generic parameter ’T’ is not used in function signature

    • 9、基本上可以使用 enum SomeEnum<T,U,V> 来声明 multi-payload 风格的枚举,这样就能正常运行。这用来提示未完成的指令寄存器(IR)引发的错误。

      • 在 Objective-C 的枚举类型导入到 Swift 时,已经废弃的枚举元素将不会影响可用元素的使用,这个可能需要 Swift 中一些枚举名称的改变。

      • 从 C 中导入的枚举类型都表示为 RawRepresentable,这包括哪些没有被声明为 NS_ENUMNS_OPTIONS 枚举值,所有这些枚举类型中的 value 属性都需要重名为 rawValue.

    • 10、方法和函数现在使用同样的参数命名规则了,我们可以用 “_” 符号来省略一个外部的参数名,为了简化使用,用来指定参数名的简化符号 “#” 被移除,因为 Swift 为默认参数提供了特殊的规则:

      • 声明:

            func printFunction(str:String, newline:Bool)
            func printMethod(str:String, newline:Bool)
            func printFunctionOmitParameterName(str:String, _newline:Bool)
        
      • 调用:

            printFunction("hello", newline:true)
            printMethod("hello", newline:true)
            printFunctionOmitParameterName("hello", true)
        
    • 11、条件循环语句 do/while 循环被重名为 repeat/while。关键字 do 目前用来引入一个新的作用域(这对新引进的错误处理和 defer 关键字很重要)。

          Swift 1.2:
      
              do {
                  ...
              } while <condition>
      
          Swift 2.0:
      
              repeat {
                  ...
              } while <condition>
      
    • 12、打印语句的改变,在 Swift1 中,有 println()print() 两个在控制台打印语句的方法,前者是换行打印,后者是连行打印。在 Swift 2 中,println() 已成为过去,取而代之的是他俩的结合体。

          Swift 1.2:
      
              func print(<stuff to print>)
              func println(<stuff to print>)
      
          Swift 2.0:
      
              func print(<stuff to print>, appendNewline:Bool = true)
      
          如果你想做连行打印,现在需要这样写:
      
              print("我要换行!", appendNewline: true)
      
    • 13、Swift 的文本注释(doc comments)换成了 Markdown 语法格式,与 Playgrounds 统一(Playgrounds 注释格式源于功能有限的 reStructured Text)。

          参数纵览语法:
      
              ‐ Parameters:
                  ‐ x:...
                  ‐ y:...
      
          单独参数语法:
      
              ‐ parameterx:...
              ‐ parametery:..
      
          返回值:
      
              ‐ returns:...
      
          其他需要在 QuickHelp 中高亮的语法字段,可以参考 Markdown 语法。
      
    • 14、在 Swift 中增加了 @objc(propertyName) 属性,当该属性导入到 Objective-C 时可以采用这个 propertyName 作为 getter/setter 访问器的默认名,例如:

          class MyClass:NSObject {
      
              // Objective‐C 属性被命名为 “theProperty”
              @objc(theProperty) property:String
      
              // Objective‐Cgetter 访问器被命名为 “theProperty”
              // Objective‐Csetter 访问器被命名为 “setTheProperty:”
          }
      
    • 15、注册通知由

              var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
              var acceptAction = UIMutableUserNotificationAction()
      
              acceptAction.identifier = "ACCEPT_IDENTIFIER"
              acceptAction.title = "Accept"
              acceptAction.activationMode = UIUserNotificationActivationMode.Foreground
              acceptAction.destructive = false
              acceptAction.authenticationRequired = false
      
              var inviteCategory = UIMutableUserNotificationCategory()
      
              inviteCategory.identifier = "INVITE_CATEGORY"
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Default)
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Minimal)
      
              var categories = NSSet(object: inviteCategory)
              var mySettings = UIUserNotificationSettings(forTypes: types, categories: categories as Set<NSObject>)
      
              UIApplication.sharedApplication().registerUserNotificationSettings(mySettings)
              UIApplication.sharedApplication().registerForRemoteNotifications()
      
          修改为:
      
              let acceptAction = UIMutableUserNotificationAction()
      
              acceptAction.identifier = "ACCEPT_IDENTIFIER"
              acceptAction.title = "Accept"
              acceptAction.activationMode = UIUserNotificationActivationMode.Foreground
              acceptAction.destructive = false
              acceptAction.authenticationRequired = false
      
              let inviteCategory = UIMutableUserNotificationCategory()
      
              inviteCategory.identifier = "INVITE_CATEGORY"
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Default)
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Minimal)
      
              let categories = NSSet(object: inviteCategory) as! Set<UIUserNotificationCategory>
              let mySettings = UIUserNotificationSettings( forTypes: [.Alert, .Badge, .Sound], categories: categories)
      
              UIApplication.sharedApplication().registerUserNotificationSettings(mySettings)
              UIApplication.sharedApplication().registerForRemoteNotifications()
      
  • 内部的可见性:

    • 这解决了单元测试中的一个较大的难点。以前的做法:

      • Swift 文件包含在 test target 中。现在不同的模块中有重复的类的定义,出现无法将 “X” 转换为 “X” 这样非常可怕的错误,有时会无法执行特定的测试。

      • 在测试中引入引入主程序(main program)作为一个模块。现在一切都声明为 public,所以对于测试来说都是可见的,有时候也包括应该声明为 private 的内部细节。

    • 现在可以启用 testability,它就像 C# 中的 InternalsVisibleTo。主应用程序目标模块的内部细节对测试模块可见。

      • 在对应用或框架的测试设置中,启用 testability。
      • 在单元测试中,使用 @testable import { ModuleName }
    • 这将导致测试忽略某些优化行为并保留稍后导入到测试模块中的那些内部符号。官方文档警告说,由于阻止了某些优化,因此这只适用于调试和测试版本。

  • 模式匹配:

    • Switch 语句的模式匹配(pattern matching)语法和 “if let ..., .... where” 语法一直在推广。可以在任何控制流中使用逗号操作符和 where 条件语句。还可以使用新的 case 条件语句,例 - 如:if case .Silly(let a) { }。还有一种用于 Optional<T> 的特殊形式:if case let a? = anOptional { }

      • 模式匹配在循环语句中也可以使用:for case let thing? in array { }。 这又是值得单独成文的另一个特性。

      • 类型标注不能用于模式匹配,而需要作为标注声明的一部分:

        • 这意味着,以前的这样的写法:

          • var (a:Int, b:Float) = foo()
        • 需要被重构为:

          • var (a, b):(Int, Float) = foo()
        • 其实这个改动原因是为了和元组用法相区分。

  • 错误处理:

    • NSError 变成 throw。这不是我们一贯所认识的异常,这是一个使函数提前返回 Result 的操作,单隐藏了所有提前返回的对象,也隐藏了错误解析(error unwrapping)过程等内容。

          let systemAttributes: [NSObject: AnyObject]?
      
          do {
      
              systemAttributes = try NSFileManager.defaultManager()
                                                  .attributesOfFileSystemForPath(documentDirectoryPath.last!)
      
          } catch _ {
      
              systemAttributes = nil
          }
      
          它完美地与 Objective-C 进行互操作,Swift 语言中,将标记为 throws 的方法作为选择器。这是使用 NSError 的方法,
          -(BOOL or nullable type)someMethodTakingParam:(type)param error:(NSError **),
          这种样式会自动引入标记为 throws 的方法。
      
    • 应该明白的是这并不像 Java 中已经被检查过的异常(checked exception)那样。Swift 语言并不关心异常的类型,或者处理或者不处理。这又是值得单独成文的另一功能特性。

  • guard 语句块:

    • 显式地声明你要恒成立的条件语句,恒成立时跳过整个 guard 语句。这样做的好处是绑定在 guard 语句的变量在函数的其他部分也可用。这就避免了将所有的东西都围绕一条 if 语句嵌套使用来解析(unwrap)可选类型的变量。执行到函数中 guard 语句中的 else 部分,函数一定会退出并抛出异常。也可能会调用带有 @noreturn 标记的函数。
  • Defer 关键字:

    • 关键字 defer 也很重要,因为它可以取代传统 C 风格的 “if (err) goto cleanup”。获得资源后接着就是 defer { release_resource() }。然后不管函数返回结果如何,获得的资源都将被清理。这也意味着资源的释放紧随获取资源之后。这看起来不起眼儿,实则很重要。
  • NS_OPTIONS 和 OptionSetType:

    • NS_OPTIONS 类型现在遵循 OptionSetType 协议,这样可以避免 set 样式的接口调用。位操作枚举(bitwise enumeration)与数组风格的语法相结合,而不使用管道符 “ | ” 按位操作,并且具有所有范围的集合操作功能。检查一下是否具有 contains 功能的标志,或能够执行像 isSubsetOfisDisjointWith 等这样集合操作的其他功能。这是显著的改进,表达了不直接对位进行操作的意愿。

    • 避免采用如下位运算的调用方式:

          // Swift1.2:
      
              object.invokeMethodWithOptions(.OptionA | .OptionB)
              object.invokeMethodWithOptions(nil)
      
              if options & .OptionC == .OptionC {
      
                  //.OptionC 被设置
              }
      
    • 选项设置支持字面量语法和 set 样式的调用,如 contains:

          object.invokeMethodWithOptions([.OptionA, .OptionB])
          object.invokeMethodWithOptions([])
      
          if options.contains(.OptionC) {
      
              //.OptionC is set
          }
      
    • 这种变化意味着位操作枚举实际上不再是枚举了。将这些位操作枚举声明为结构体,实现 OptionSetType 协议,提供 rawValue 属性。并且创建值作为结构体的静态成员。Swift 便会搞定其余的一切,自动提供所有集合的操作。

    • 在 Swift 中一个新的 Option 设置类型可以采用结构体遵循 OptionSetType 协议的方式编写。如果该类型中指定了一个 rawValue 属性和 static let 的常量定义,那么标准库将会为其他选项提供默认实现:

          structMyOptions:OptionSetType {
      
          let rawValue:Int
              static let TuringMachine = MyOptions(rawValue:1)
              static let LambdaCalculus = MyOptions(rawValue:2)
              static let VonNeumann = MyOptions(rawValue:4)
          }
      
          let churchTuring:MyOptions = [.TuringMachine, .LambdaCalculus]
      
  • 协议扩展:

    • 在 Swift 1.0 时代,协议(Protocol)基本上类似一个接口,定义若干属性和方法,供类、结构体、枚举遵循和实现。在 Swift 2.0 中,可以对协议进行属性或者方法的扩展,和扩展类与结构体类似。,包括与类型约束有关的通用协议。还可以自己提供协议的默认实现。这让我们开启了面向协议编程的篇章。先前,你不能,你说:“我要使用方法 X 来扩展 CollectionType,但只有集合中的类型满足某些条件才可以”。现在,你可以这么做,并且很多像 map,filter 和 sort 这样的全局函数已经进行了扩展。这样就解决了很多痛点,这也是值得单独成文的内容。同时,要看看 WWDC 的面向协议编程(Protocol Oriented Programming)了解一些细节。

    • Swift 中,大多数基础对象都遵循了 CustomStringConvertible 协议,比如 Array、Dictionary(Swift 1.0 中的 Printable 协议),该协议定义了 description 方法,用于 print 方法打印对象。现在我们对该协议扩展一个方法,让其打印出大写的内容:

          var arr = ["hello", "world"]
      
          print(arr.description)         // "[hello, world]"
      
          extension CustomStringConvertible {
              var upperDescription: String {
                  return "(self.description.uppercaseString)"
              }
          }
      
          print(arr.upperDescription)    // "[HELLO, WORLD]"
      
    • 如果在 Swfit 1.0 时代,要想达到上述示例的效果,那么我们需要分别对 Array、Dictionary 进行扩展,所以协议的扩展极大的提高了我们的编程效率,也同样使代码更简洁和易读。

  • available 检查:

    • 作为 iOS 开发者,谁都希望使用最新版本 iOS 的 Api 进行开发,省事省力。但常常事与愿违,因为我们经常需要适配老版本的 iOS,这就会面临一个问题,一些新特性特性或一些类无法在老版本的 iOS 中使用,所以在编码过程中经常会对 iOS 的版本做以判断,就像这样:

          if NSClassFromString("NSURLQueryItem") != nil {
              // iOS 8 或更高版本
          } else{
              // iOS8 之前的版本
          }
      
    • 以上这只是一种方式,在 Swift 2.0 之前也没有一个标准的模式或机制帮助开发者判断 iOS 版本,而且容易出现疏漏。在 Swift 2.0 到来后,我们有了标准的方式来做这个工作:

          if #available(iOS 8, *) {
      
              // iOS 8 或更高版本
              let queryItem = NSURLQueryItem()
      
          } else {
              // iOS8 之前的版本
          }
      
    • @available 属性自 Swift 1.2 就存在了并且后续支持得很好。添加了一个新的陌生语法 if #available(),为处理版本检查提供了支持。而不是插入你喜欢的方法。

    • 遗憾的是你不能只声明一个属性 UISearchController 并将 target 设置为 iOS 7,然后只允许访问类中的属性。Swift 希望整个类的定义都可以或者不可以。也可以不再采用协议,除非支持 target设置中所有的操作系统版本,除非将整个类标记为只在更新的操作系统版本可用。

    • 这意味着使用 if #available() 存在单独的子类和对创建适当对象的保护。尽管如此,我个人还是发现了一个 Bug,应用在 iOS 4.0-4.1 发生崩溃,由于编译器没有发出警告,方法只在 iOS 4.2 才引入,因此我犹如与定时炸弹相伴。

  • C 函数指针:

    • Swift 现在可以使用 C 函数指针,CFunctionPointer<T -> U> 类型被移除,C 函数现在使用新的 @convention(c) 属性声明,和其他函数类型一样,@convention(c) T -> U 是一个非空的除非是它是可选的。任何全局函数,嵌套函数和不捕获状态的闭包都可以作为一个 C 函数指针直接传递。你也可以调用来自 C 程序的函数。

    • 你可以显示地使用新属性 @convention(c),表示函数应该使用 C 调用约定,简单痛快!尽管我想不出在此对块(block)的支持有何用,作为所发生变化的一部分,@objc_block 也被删掉了,使用 @convention(block) 取而代之。@convention(swift) 默认支持所有函数和闭包。

  • API 审计:

    • 大量的 API 已经进一步进行了审计而更合理。举几个例子:

      • UITableView 的 dequeueReusableCellWithIdentifier 方法现在返回 UITableViewCell? 类型的对象。

      • UIKit 的属性现在也被声明为了实际的属性。

      • translatesAutoresizingMaskToConstraints = false 代替了 setTranslatesAutoresizingMaskToConstrains(false)

  • 库:

    • 这并不是编程语言所特有的。iOS 9 含有不同版本的 Swift 标准库,并且在未来系统中将添加修正后的 Swift 标准库。结合新的 App Thining 技术,下载过程中苹果商店会将 Swift 标准库剥离出去的。我仍然在追根溯源地探求这究竟是如何工作的。
  • 遗漏:

    • 明显的一个遗漏是处理异步代码。苹果公司为我们提供了 GCD,这是一个强大的基础类库,可以构建很多异步操作和并发原语。然而,这些天我们做的每件事,构建用户接口和 API 都需要考虑异步性和并发性。我们把一个文件读操作锁定一段时间,对用户来说整个世界就都静止了。这是个持续的痛点,不是多大的事儿,但如果经常性地每天重复,恐怕也是不行的。C# 和 JavaScript 都采用了 async/await 来为异步代码提供一流的语言支持。我想很多人都想知道,Swift 会提供什么样的语法糖来帮助我们在实现异步操作方面确保正确性。

前言

  • Swift 全面支持 Unicode 符号。
  • Swift 中的定义和实现是在同一个单元中的,通常一个 Swift 源代码单文件是以 “.Swift” 结尾的。
  • Swift 不需要单独编写一个 main 函数作为入口,在 Swift 语言中函数是一等成员,编译器会自动将遇到的第一个函数作为入口。
  • Swift 允许我们不用在行尾加分号 “;”。但如果在同一行有两个甚至多个表达式,需要在每个表达式后面加上分号。

  • Playground 是一种编写代码时可以即时预览代码运行效果的功能。使用 Playground 后,在实际项目中可以为我们节省不少功能调试和函数测试时间,这些时间完全可以从事其他创造性的活动。

  • Swift 常用标注:

    • // MARK: 添加注释说明,加 “-” 添加分割横线
    • // FIXME: 表示此处有 bug 或者要优化
    • // TODO: 一般用于写到哪了做个标记,然后回来继续

Hello Word

在屏幕上打印“Hello, world”,可以用一行代码实现:

print("Hello, world")

你不需要为了输入输出或者字符串处理导入一个单独的库,你也不需要main函数,你同样不需要在每个语句结尾写上分号。

1、语法速览

var myVariable = 42
myVariable = 50
let myConstant = 42

变量定义用var,常量则用let,类型自行推断。

 

let apples = 3
let oranges = 5
let appleSummary = "I have (apples) apples."
let fruitSummary = "I have (apples   oranges) pieces of fruit."

用括号包含变量

 

let quotation = """
I said "I have (apples) apples."
And then I said "I have (apples   oranges) pieces of fruit."
"""

代码通过三个双引号来包含预定格式的字符串(包括换行符号),左侧缩进空格省略。

 

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

数组和字典集合初始化符合常规,字典后面可以保留逗号结尾

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

初始化函数也比较简洁。

 

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore  = 3
    } else {
        teamScore  = 1
    }
}
print(teamScore)

控制流的if-else这些和其他语言没有什么差异,for ... in 则是迭代遍历的语法,控制流方式还支持其他的while、repeat...while等不同的语法。

 

var optionalString: String? = "Hello"
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, (name)"
}

这部分则是可空类型的使用,以及可空判断语句的使用,可空判断语句在Swift中使用非常广泛,这种相当于先求值再判断是否进入大括符语句。

 

let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy (x)?")
default:
    print("Everything tastes good in soup.")
}

Switch语法和常规的语言不同,这种简化了一些语法,每个子条件不用显式的写break语句(默认就是返回的),多个条件逗号分开即可公用一个判断处理。

 

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)

上面字典遍历的方式采用for...in的方式进行遍历,另外通过(kind, numbers)的方式进行一个参数的解构过程,把字典的键值分别付给kind,numbers这两个参数。

 

var total = 0
for i in 0..<4 {
    total  = i
}
print(total)

上面的for...in循环采用了一个语法符号..<属于数学半封闭概念,从0到4,不含4,同理还有全封闭符号:...全包含左右两个范围的值。

 

func greet(person: String, day: String) -> String {
    return "Hello (person), today is (day)."
}
greet(person: "Bob", day: "Tuesday")

上面是函数的定义,以func关键字定义,括号内是参数的标签、名称和类型内容,返回值通过->指定。

上面函数需要输入参数名称,如果不需要参数名称,可以通过下划线省略输入,如下

func greet(_ person: String, on day: String) -> String {
    return "Hello (person), today is (day)."
}
greet("John", on: "Wednesday")

另外参数名称可以使用标签名称。

func greet(person: String, from hometown: String) -> String {
    return "Hello (person)!  Glad you could visit from (hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill!  Glad you could visit from Cupertino."

嵌套函数如下所示。

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y  = 5
    }
    add()
    return y
}
returnFifteen()

复杂一点的函数的参数可以传入函数进行使用,这种类似闭包的处理了

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

下面是一个闭包的函数,闭包通过in 来区分参数和返回的函数体

numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})

 

 

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

类的定义通过class关键字进行标识,默认的权限是internal,在项目模块内部可以访问的,非常方便。

使用则如下所示,可以通过点语法直接获取属性和调用方法。

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

 

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

类通过使用init的指定名称作为构造函数,使用deinit来做析构函数,使用self来获取当前的类引用,类似于其他语言的this语法,super获取基类的引用。

其他的处理方式如继承、重写的语法和C#类似。

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length (sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

 

类的属性使用get、set语法关键字,和C#类似

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

 

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }

类属性的赋值可以进行观察,如通过willSet在设置之前调用,didSet在设置之后调用,实现对属性值得监控处理。

 

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king
    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
let aceRawValue = ace.rawValue

和类及其他类型一样,枚举类型在Swift中还可以有方法定义,是一种非常灵活的类型定义,这个和我们之前接触过的一般语言有所差异。

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at (sunrise) and sunset is at (sunset).")
case let .failure(message):
    print("Failure...  (message)")
}

 

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The (rank.simpleDescription()) of (suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

结构类型和类的各个方面很类似,结构支持构造函数,方法定义,属性等,重要一点不同是结构在代码传递的是副本,而类实例传递的是类的引用。

 

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

这里的协议,类似很多语言的接口概念,不过比常规语言(包括C#)的接口更加多样化、复杂化一些。

Swift的协议,可以有部分方法实现,协议可以可选,继承其他协议等等。

 

extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number (self)"
    }
    mutating func adjust() {
        self  = 42
    }
}
print(7.simpleDescription)

扩展函数通过extension进行标识,可以为已有的类进行扩展一些特殊的方法处理,这个类似C#的扩展函数。

 

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

异常处理中,函数声明通过throws关键字标识有异常抛出,在函数里面通过throw进行异常抛出处理。

而在处理有异常的地方进行拦截,则通过do...catch的方式进行处理,在do的语句里面,通过try来拦截可能出现的异常,默认catch里面的异常名称为error。

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}

可以对多个异常进行判断处理

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: (printerError).")
} catch {
    print(error)
}

还可以通过使用try?的方式进行友好的异常处理,如果有异常返回nil,否者获取结果赋值给变量

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

 

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
print(fridgeIsOpen)

使用defer的关键字来在函数返回前处理代码块,如果有多个defer函数,则是后进先出的方式进行调用,最后的defer先调用,依次倒序。

 

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)

Swift支持泛型,因此可以大大简化很多函数的编写,提供更加强大的功能。

enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

 

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    return true
                }
            }
        }
        return false
}
anyCommonElements([1, 2, 3], [3])

泛型的参数支持where的关键字进行泛型类型的约束,如可以指定泛型的参数采用什么协议或者继承哪个基类等等。

 

2、Swift 2.2 新特性

  • 允许更多的关键字用做参数名:

    • 好的参数名对于提高代码可读性很重要。在 Swift 中很多关键字比如 in,repeat,defer 都不能用做参数名。2.2 中,除了少数修饰参数的关键字外都将允许用作参数名。

      澳门新萄京官方网站 1

  • 为 Tuples 增加对比操作符:

    • 当前,Tuples 的数据不能使用 == 操作符,2.2 中将支持 Tuples。

      澳门新萄京官方网站 2

  • 关联已存在类型时,不再使用 typealias:

    • typealias 现在有两个用处:
      • 为一个已经存在的类型取个别名
      • 在协议中作为一个类型的占位名称
    • 代码如下:

      澳门新萄京官方网站 3

    • 这是两种完全不同的用法,不应该用一样的关键字。2.2 中将第一种情况时,启用新的关键字 associatedtype

  • 函数签名将包括参数名:

    • 一个函数有相同的函数名,参数名不同有多个重载很常见。当有多个重载时,在调用时能够通过参数名来区别。但是在获取类型时,却不包括参数名。

    • 举例 UIView 中有这么几个方法:

      澳门新萄京官方网站 4

    • 使用时可以通过参数名区分:

      澳门新萄京官方网站 5

    • 但是这样使用时却会报错,2.2 中将会解决这个问题。

      • let fn = someView.insertSubview // ambiguous: could be any of the three methods
  • 一个新的方法生成 selector:

    • 现在为了生成 OC 下使用的 selector 只能使用字符串生成,没有类型检查,很容易造成失误。将提供一个 #selector() 方法生成 selector,如下:

    • let sel = #selector(UIView.insertSubview(_:at:)) // produces the Selector "insertSubview:atIndex:" 增加 #if swift 语法判断当前 swift 版本

    • 使用如下:

      澳门新萄京官方网站 6

1、Swift 基本数据类型

  • 1)变量与常量

    • 在 Swift 语言中声明变量使用 var 关键字,声明常量使用 let 关键字。

          // 定义 String 类型的字符串  str
          let str:String = "hello world"
      
    • 声明时类型是可选的,如果在声明时没有指定类型且对变量赋了初值,编译器会自动推断常量或者变量的类型,这种机制被称为 “类型推断”。如果在声明时指定了类型又赋了初值,那么指定的类型必须和赋给它们的值一样,Swift 是一门强类型语言,不能将变量本身类型之外的值赋值给它。如果没有赋给初值,务必声明变量或者常量的类型,并用冒号充当分隔符,否则编译会报错。

    • Swift 语言将具体的某种类型的值称之为类型字面量。例如 let num = 2.8 中的 "2.8" 就是浮点类型字面量。

  • 2)整型

    • Swift 语言拥有继承自 C 语言的有符号类型 Int、Int8、Int16、Int32、Int64,以及无符号整形 UInt、UInt8、UInt16、UInt32、UInt64。其中 IntUInt 类型的字长始终和当前平台的原生字长相同,即 32 位系统下声明获得的是 32 位的整型,64 位系统下获得的是 64 位的整型。

    • 整型的取值范围:

      • 最小值:Int8.minINT8_MIN
      • 最大值:Int8.maxINT8_MAX
    • 整型的声明:

      • 隐式声明机制:

            // 自动调用构造函数
            let intNum:Int = 12
        
      • 显式声明机制:

            // 显式的调用初始化构造器
            let intNum = Int.init(22)
        
    • 其他方法或属性:

          // 计算两个数字之间的距离(两数之差)
          num.distanceTo(15)
      
          // 访问变量或常量的字符串版本
          num.description
      
          。。。。。
      
  • 3)浮点型

    • Swift 语言为我们提供了两种有符号浮点数类型,FloatDoubleFloat 是 32 位浮点数类型,Double 是 64 位浮点数类型。当使用类型推断声明一个浮点型变量或者常量时,变量或常量总是默认被推断为类 Double 型。

    • 浮点型的声明:

          let floatNum:Float = 2.1
      
          // 默认被推断为 Double 型
          let doubleNum = 2.2`         
      
  • 4)布尔型

    • Swift 语言中,布尔型只有两种值,truefalse。如果在 Swift 语言中直接使用零或者非零来表示逻辑真假,编译器一定会弹出异常。可以直接在布尔变量前加 “!”,来达到布尔值取反的作用。

    • 布尔型的声明:

          let boolNum:Bool = false
      
  • 5)值类型/引用类型

    • 在 Swift 语言中,所有的类型都可以被分为 “值类型” 或者 “引用类型”,可以将其理解为函数参数传递的方式。
    • 从程序的角度来看,值类型和引用类型是相对的一个概念,其中的差别就在于:对新的对象产生赋值等指向性的操作之后,再次操作赋值对象或被赋值对象是否会同步于另外一个对象。
    • 在 Swift 语言中,大多数类型都是值类型的,但是也有一些特殊情况,比如可以在函数参数定义中使用 inout 关键字将参数定义为引用类型。

          // a,b 都是引用类型
          func swapT<T>(inout a:T, inout b:T)
      
  • 6)可选类型

    • Swift 语言为我们提供了一种全新的、更加安全的类型 —— 可选类型。可选类型是使用范型枚举的形式来组织的,也就是说此特性可以运用于所有的类型、结构体、类或者其他复杂数据类型。

    • 可选是指当一个变量、常量或者其他类中存储有值的时候返回里面存储的值,没有值的时候返回 nil。nil 不能用于非可选的变量或者常量,如果声明了一个可选的变量或者常量没有初始化,程序会默认赋值 nil。在 OC 中 nil 表示的是一个指向不存在对象的指针,而 Swift 中表示空的关键字为 “nil”,它没有其他含义。

    • 可选的声明:

      • 可选的标准声明形式是在程序中使用类型名紧跟 “ ? ”。

            var value:Int?
        
            print("(value)")
            // 或 
            print("(value?.description)")
        
            // 输出为 nil
        
      • 可选的显式声明形式。

            var value:Optional<Int>
        
            print("(value)")
            // 或 
            print("(value?.description)")
        
            // 输出为 nil
        
    • <1>、可选绑定(Optional binding):

          var value:Optional<Int> 
          if var maxValue = value { 
              maxValue  
              print("(maxValue)") 
          }
      
      • 如果 value 值为 nil,则不执行变量 maxValue 的声明,同时也不执行 if 判断语句中第一个分支的代码段,这样程序会很容易被理解,而且只需要这样简单的两行代码就避免了因为使用值为 nil 的对象导致的程序异常。
    • <2>、强制解析可选:

      • 如果确定这个可选类型中的变量肯定包含值的时候,可以使用名称紧跟 “ ! ” 的方式强制获取类型可选中的值,从而省略判断步骤。但是如果这个变量中没有值,使用强制解析可选可能会在运行期弹出异常。这种机制叫做“强制解析可选”。
    • <3>、隐式解析可选:

      • 在某些程序架构中,在特定模块中可以确定某个可选变量总是有值的,这种时候可以使用隐式解析可选解析可选,隐式解析可选用于一个确定会有值的可选类型实例声明。可以将可选变量声明中的“ ? ”改为“ ! ” 来标注一个隐式解析可选。

            var nullValue:String! = "Not Null String"
            print(nullValue)
        
    • <4>、可选运算符:

      • 可选运算符 “ ?? ” 的执行逻辑是表达式 var value = a ?? b 中当操作数 a 不为 nil 时表达式返回操作数 a 的值,当操作数 a 为 nil 时表达式返回操作数 b 的值。

            var value1:Int?
            var value2 = 3
            var value = value1 ?? value2
        
            print(value)
        
    • <5>、可选链:

      • 就是将可选的调用链接在一起形成一个链,如果任何一个节点为空(nil),将导致整个链失效。而不会引发强制解包可选时发生的错误。可选链可以多层可选。
  • 7)泛型

    • 使用同样的操作可以应用于不同的数据类型。泛型编程的实现是我们程序在另一种抽象层次上的提升。类是现实世界事物的抽象,而泛型则是现实世界行为的抽象。在 Swift 语言中,泛型可以说是用的最广最强大的特性之一,因为在 Swift 语言本身的语言底层大量的使用了泛型。

    • 泛型同其他语言相通,用“< >” 符号来声明泛型。

          func  <T : _IntegerArithmeticType>(lhs: T, rhs: T) -> T
      
    • 泛型使得我们能够在编写好一份代码之后,应用于多种数据类型,甚至为了使用安全起见,我们还能限制响应的泛型数据类型,必须遵从某些约束。

          func swapT<T>(inout a:T, inout b:T)
          struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible { }
      
  • 8)元组

    • 可以通过使用元组,把多个不同类型的值组合在一起,组成一个复合值。元组中的元素类型可以是相同的,也可以是不同的。元组的声明中也可以使用类型推断。元组可以用作函数返回值,它可以使函数能一次返回更多的信息。

      • 元组的声明:

        • 标准声明:

              // 定义时指定元组名称、元素名称并且初始化
              let myProject = (oneElement:"game", twoElement:2048)
          
        • 匿名声明:

              // 声明一个匿名的元组
              let (appType, appName) = ("game", 2048)
          
      • 元组中元素的访问:

        • 标准声明的元组:

              // 使用元素名访问
              print(myProject.oneElement)
          
              // 使用元素在元组中的顺序下标访问
              print(myProject.0)
          
        • 匿名声明的元组:

              // 使用元素名访问
              print(appType)
          
  • 9)枚举

    • 枚举是一种自定义的数据类型,在 Swift 中枚举类型拥有相当高的自由度。在 Swift 语言中枚举是一级类型,它拥有在其他语言中只有类才拥有的一些特性,比如实例方法,实例构造器等。枚举定义了一个常用的具有相关性的一组数据,并在你的代码中以一个安全的方式使用它们。

    • 一个枚举通常包含多个枚举成员,枚举成员可以包括计算型属性、类型别名,甚至其它枚举、结构体和类。枚举声明中,每一个事件块都由一个 case 关键字开始。多个成员的值可以出现在一行上,用逗号分隔。

    • 枚举是值类型,并且只有在赋予变量或常量,或者被函数调用时才被复制。

    • <1>、标准定义格式:

          enum enumerationName {
      
              case enumerationCase1
              case enumerationCase2
              .....
          }
      
          enum PointRect {
      
              case top
              case bottom
              case left
              case right
          }
      
    • <2>、和 C 语言不同的是,标准的枚举定义方式成功定义枚举后,成员值并不会隐式被指定为 0、1、2、…… 这种形式。如果需要在定义时指定初始值,我们可以使用另一种形式。在声明的时候赋予的值叫做原始值(raw value),这些值的类型会自动进行判断,原始值必须是字面上的整数、浮点数、字符或者字符串。如果原始值类型变量被指定为整型,则不必为每个成员显示地指定值,它们会被隐式的被标为值 0、1、2 、…… 等。

      • 带原始值的声明形式:

            enum enumerationName: rawValueType {
        
                case enumerationCase1 = rawValue1
                case enumerationCase2 = rawValue2
                .....
            }
        
            enum PointRect:Int {
        
                case top
                case bottom = 2
                case left
                case right
            }
        
            // 枚举原始值类型变量被指定为整型,只显示的指定一个值,其它会被隐式的指定值(top == 0, left == 3, right == 4)
            print(PointRect.top.rawValue)           
        
    • <3>、另外可以通过为每个枚举成员设定一个或多个关联值,从而使用枚举来存储和维护一些特别的数据。

          enum PointRect {
      
              case top(Int, Int)
              case bottom(Int, Int)
              case left(Double, Double)
              case right(Double, Double)
          }
      
    • <4>、枚举与类和结构体的关系:

      • 枚举与其他两者最大的相同之处就在于都可以定义方法。而其他的更多特性,对于枚举基本没有,没有属性,每一个枚举值都是常量。枚举中所定义的方法也基于对本身值的操作,无法定义一些无关的属性和操作。
  • 10)结构体

    • 结构体是值类型的,其实例将会在被赋予变量或者常量和被函数调用时被赋值。

    • 标准定义格式:

          struct structName {
      
              var 成员1: 数据类型 1
              var 成员2: 数据类型 2
              .....
          }
      
          struct BookInfo {
      
              var ID:Int = 0
              var Name:String = "Default"
              var Author:String = "Default"
              var RootType:String = "Default"
          }
      
  • 11)类型别名

    • 在 Swift 语言中使用 typealias 定义类型别名。

          typealias ShortInteger = Int8
      
  • 12)类型转换

    • 隐式类型转换:如 C 语言的类型转换
    • 显式类型转换:Swift 语言是一种强类型语言,其整型的强制类型转换就是调用了参数类型对应的整形扩展构造方法,然后通过对应扩展构造方法的处理返回一个当前整形字长的整形值。

          // 将字符型转换成整型
          Int(12.4)
      

数据类型

Swift 包含了以下基础数据类型:Int 表示整型;DoubleFloat 表示浮点型;Bool 表示布尔;String 表示字符串。Swift 还提供了三个基本的集合类型,Array 数组,Set 集合和 Dictionary 字典。

Swift 还增加了高阶数据类型比如元组(Tuple)。元组可以让你创建或者传递一组数据,比如作为函数的返回值时,你可以用一个元组可以返回多个值。

Swift 还增加了可选(Optional)类型,用于处理值缺失的情况。可选表示“那儿有一个值,并且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用nil,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的nil指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。

非可选类型,要么赋值,要么不赋值(不赋值的话,默认不会等于nil);换句话说,非可选类型永远不能为 nil

可以在类型后面加?声明可选类型。调用时它可能有值,也可能是nil,可以用变量!的形式保证变量有值。

可以在类型后面加!声明可选类型。调用时它可能有值,也可能是nil,区别于上面,直接用变量的形式已经保证变量有值了。

你可以使用可选的(Optional)元组反映整个元组可以是 nil 的情况。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 (Int,Int)?(String,Int,Bool)?

可选元组类型如(Int,Int)?与元组包含可选属性如(Int?,Int?)是不同的。可选的元组类型,整个数组是可选的,而不只是元组中的每个元素值。

2、运算符

  • Swift 语言支持大部分标准 C 语言的运算符,并且改进了许多特性来使我们的代码更加规范,其中主要包含算数运算符、区间运算符、逻辑运算符、关系运算符、赋值运算符、自增自减运算符、溢出运算符等。

    澳门新萄京官方网站 7

  • 1)组合赋值运算符:是将其他运算符和赋值运算符组合在一起执行的运算。算数自反赋值运算符属于组合赋值运算符。

    • 要实现一个组合赋值符号需要把运算符的左参数设置成 inout 类型,从而使运算符函数体内部可以直接修改他的值。

          func  = (inout lhs: Int, rhs: Int) {
              lhs = lhs   rhs
          }
      
  • 2)自定义运算符:自定义运算符是新的编程语言才支持的特性,不同于组合赋值运算符,你可以使用 / = - * % < > ! & | ^ ~ 来组合构成新的运算符。

    • 自定义一个运算符通常需要先声明再实现其功能,声明自定义的运算符需要使用 operator 关键字。

          operator      :表示要进行运算符重载
          infixpostfix  :表示这是一个二元运算符,操作符在两个操作数中间。 
          prefix        :表示这是一个一元运算符,操作符在操作数前边。
          postfix       :表示这是一个一元运算符,操作符在操作数后边。
          associativity :结合性,包含 left(左结合)、right(右结合)和 none(自动),默认值为 none。
          precedence    :优先级,默认为 100,可省略。
      
          // 声明自定义运算符 <>
          infix operator <> {associativity none precedence 100}
      
          // 实现自定义的运算符 <>
          func <> (lhs: Int, rhs: Int) -> Int {
      
              return (lhs   rhs) * (lhs - rhs)
          }
      
          // 输出值等于 20
          let n1 = 6;   let n2 = 4;   let value = n1 <> n2;   print(value)
      
  • 3)运算符重载:让已有的运算符对自定义的类和结构进行运算或者重新定义已有运算符的运算规则,这种机制被称为运算符重载。同一个运算符在处理不同数据类型时,实现的是不同的功能。

        // 声明运算符
        infix operator >< { associativity left }
    
        // 实现运算符
        func >< (inout leftValue:String, inout rightValue:String) -> String {
    
            var tmp = leftValue
            leftValue = rightValue
            rightValue = tmp
    
            return tmp
        }
    
    • 默认的赋值符 “=” 和三目条件运算符( ? : )是不可重载的。

    • Swift 语言和其他高级语言不同,其原生的关系运算符不能判断自定义的类型是否相等,所以我们需要重载自定义的类和结构的比较符 “==”或 “!=”。

          func == (left: CenterPointer, right: CenterPointer) -> Bool {
              return (left.x == right.x) && (left.y == right.y)
          }
      
          func != (left: CenterPointer, right: CenterPointer) -> Bool {
              return  !(left == right)
          }
      
  • 4)运算符优先级和结合性:运算符的优先级使得一些运算符优先于其他运算符,从而使得高优先级的运算符会先被计算。结合性用于定义相同优先级的运算符在一起时和表达式结合或关联的规则。

    • 结合性(associativity)包含 left(左结合)、right(右结合)和 none(自动),结合性的默认值为 none。优先级( precedence )默认为 100。

          // 指定运算符的优先级和结合性 左结合 优先级 140
          infix operator  - {associativity left precedence 140}
      

      澳门新萄京官方网站 8

nil

你可以给可选变量赋值为nil来表示它没有值

3、表达式

  • Swift 语言使用表达式来表示程序中的最小单位,通常一个表达式可以由数字、字符、运算符、变量、常量、函数调用等可以求得值的有意义的排列组成的组合。根据组合方式的不同,表达式可以分为基本表达式、多元表达式、前缀表达式、后缀表达式。

  • 1)基本表达式:

    • self 表达式:用于对当前类型或者类型实例自身进行引用,从而访问其内部成员。

          self.menberFunc
      
    • super 表达式:超类表达式,也可以理解为父类,用于访问当前类或者实例的父类成员或者方法。

          super.menber
      
    • 隐式成员表达式:用于在可以推断出类型的上下文中引用这个类型的成员。

          var poNum = SomType.max
          poNum = .min
      
    • 圆括号表达式:用于划分运算符优先级和创建元组,通常由一对圆括号和若干个自表达式和逗号共同构成。

          (表达式1, lab2:表达式2, lab3:表达式3, ...)
      
    • 通配符表达式:主要使用符号 “_” 来忽略表达式中的某个参数,这和正则表达式的通配符的概念是不同的。

          (a, _) = (1, 2)
      
  • 2)前缀表达式:

    • 函数调用表达式:通常由函数名加上参数列表组成。

          FuncName(value1, lab2:value2)
      
    • 初始化函数表达式:即某个类型用于初始化其实例的函数表达式。

          SomeClass.init
      
    • 显式成员表达式:是显式的访问类型、元组或者其他模块成员变量的一种方式。

          var cat:Tanimal()
          var iFoots = cat.hasfoot
      
    • 后缀 self 表达式:通常有两种形式的后缀表达式。

      • 1、表达式.self 这种形式的表达式返回表达式的自身的值。
      • 2、类型实例.self 这种形式的表达式返回当前实例所属的类型,通常用于需要动态获取实例类型的场景中。
    • 动态类型表达式:专门用于动态获取类型的表达式。
      • 标准形式是:表达式.dynamicType ,其中表达式不能为类型名称。
      • 可以通过使用 .dynamicType 获得当前实例对象所属的类型,并访问其类方法。
    • 附属脚本表达式:可以通过附属脚本表达式访问 getter/setter 的方法,他的基本形式是:表达式1 [index 表达式2]

    • 强制取值表达式:使用 “!” 来强制获取某个不为 nil 的可选表达式的值。

    • 可选链表达式:使用 “?” 来声明一个可选类型变量或者对象,当对象不为 nil 时就可以访问对象的方法或者成员。

      • 在一个后缀表达式的子表达式中,有一个可选表达式,那么只有最外层的表达式返回的才是一个可选值。

声明常量和变量

let来声明常量,用var来声明变量。

4、控制流(控制结构)

  • 指令的执行顺序在程序结构中,我们称之为控制流。控制流,也称为控制结构,通常包括:顺序结构、条件结构、循环结构、转向结构。

  • 1)条件结构(分支结构):

    • if 语句:

          if <条件表达式> {
              语句体 1
          } else {
              语句体 2
          }
      
    • switch 语句:

          switch value {
      
              case value1:
                  语句体1
              case value2:
                  语句体2
              default:
                  默认语句体
          }
      
      • 和 OC 中的 switch 语句不同,在 switch 语言中你不需要在 case 块中显式的使用 break 语句跳出 switch,当匹配到的 case 块中的代码块中的代码执行完毕后,程序会终止 switch 语句,而不会继续执行下一个 case 块。

      • 可以使用 fallthrough 在 switch 语句中使代码继续执行到下一个 case 中的代码,而不会检查它下一个将会落入执行的 case 中的条件是否匹配,从而达到和 C 语言标准中 switch 语句特性一样的效果。

      • 在 switch 语言中每个 case 块后的匹配条件可以有多个,每个匹配条件之间用逗号隔开。switch 语句不会同时匹配大些字母和小写字母。 如:case 1, 2, 3, 4, 5,

      • 在 switch 语言中每一个 case 块都必须包含至少一条语句。

      • 可以使用元组在同一个 switch 语句中匹配多个值,元组中的元素可以是值,也可以是范围。

      • switch 语句允许多个 case 匹配同一个值,不过如果存在多个可匹配分支的时候,只会执行第一个被匹配到的 case 块。

      • 像 if 语句一样,switch 语句也支持值绑定,case 块允许将匹配的值绑定到一个临时的常量或变量,这个常量或变量在该 case 块里就可以被引用了。

  • 2)循环结构:

    • for-in 循环语句:

          for 循环变量 in <范围,集合,队列...> {
      
              循环体.....
          }
      
      • 当不需要使用范围内的每一项的值时,可以使用下划线 “_” 变量名来忽略对值的访问。

      • 遍历字典时,字典的每项元素会以(key, value)元组的形式返回。

      • 循环变量不需要定义。

            for num in 0...10 {
                print(num)
            }
        
    • for 循环语句:

          for initialization; condation; increment {
      
              statements
          }
      
      • initialization:初始化表达式,condation:循环条件,increment:改变循环条件的表达式。

      • 在 Swift 2.2 中 C 语言样式的 for 循环语句被废弃,C-style for statement is deprecated and will be removed in a future version of Swift。

    • while 循环语句:

          while <条件表达式> {
      
              statements
          }
      
          do {
              statements
      
          } while <条件表达式>
      
      • 在 Swift 2.2 中 do-while 循环语句被废弃,使用 repeat-while 循环语句代替,'do-while' statement is not allowed; use 'repeat-while' instead。

            repeat {
                statements
        
            } while <条件表达式>
        
  • 3)控制转向语句:

    • continue :会通知一个循环体立即停止本次循环,直接回到循环条件判断,重新开始下次循环。

    • break :会立即中断该循环体,然后跳转到表示循环体结束的大括号后的第一行代码,即跳出本层循环体。可以在 switch 和循环结构中使用。

    • fallthrough :在 switch 语句中使代码继续执行到下一个 case 中的代码,而不会检查它下一个将会落入执行的 case 中的条件是否匹配,从而达到和 C 语言标准中 switch 语句特性一样的效果。

    • 标签语句 :Swift 语言提供了更强大的跳出机制,你可以显式的指出需要跳出的是哪一层循环或 switch 结构。为了实现这个目的,我们可以使用标签来为循环体或者 switch 代码打上标记,当需要使用 break 或者 continue 时,带上这个标签就可以控制跳出或中断的是哪一个循环或 switch 结构。

          label loopName: for number in sArray {
      
              statements
          }
      
          // 跳出 loopName 循环
          break loopName              
      

类型标注

当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。

使用 :加空格类型名在变量或常量名之后就可以完成类型注解。

5、函数

  • 函数是执行特定任务的代码块,每个函数都有一个类型,可以像使用 Swift 语言中其他类型一样使用函数类型,将函数作为参数传递给其他函数,或者将函数类型当作返回类型。在 Swift 语言中没有主函数。在 Swift 语言中函数分为两类,一种是库和框架中的函数,一种是自定义的函数。

  • 函数定义需要关键字 func,其一般格式为:

        // 使用时第一个参数名(参数名1)会被省略
        func 函数名 (参数名1:参数类型, 参数名2:参数类型 ...) -> 函数返回类型 {
    
            函数体 .....
    
            return 返回值
        }
    
  • 在 Swift 语言中,函数的形参和返回值是非常具有灵活性的,在需要的时候,可以定义一个或者多个甚至选择性的省略。实际上,在定义的时候忽略返回值等于隐式声明了函数的返回值类型为 void,而实际上函数还是返回了一个空的元组作为返回值。

  • 1)外部形参:

    • Swift 语言也能支持 OC 的函数参数标签模式,这种模式被称为外部形参。不过如果你为参数制定了外部形参名,那么在调用的时候就必须显式的使用。

      • 定义格式:

            // 使用时本地形参名(本地形参名1、本地形参名2 ...)会被省略
            func 函数名 (外部形参名1 本地形参名1:参数类型, 外部形参名2 本地形参名2:参数类型 ...) -> 函数返回类型 {
        
                函数体 .....
        
                return 返回值
            }
        
    • 如果别人第一次阅读你的代码,使用外部形参名称可以使你要表达的意思更加明确,上下文更加清清晰。在写外部形参名时,完全可以只写一次名字,只需要用一个 hash 符号“#” 作为参数名称的前缀,从而告诉 Swift,我们使用了名称相同的本地形参名称和外部形参名称。

      • 定义格式:

            // Xcode 7.3.1  Swift 2.2 中不支持该种定义方式
            func 函数名 (#参数名1:参数类型, #参数名2:参数类型 ...) -> 函数返回类型 {
        
                函数体 .....
        
                return 返回值
            }
        
  • 2)默认值形参:

    • 在 Swift 语言中可以为任何形参定义默认值以作为函数定义的一部分,如果已经定义了默认值,那么调用函数时就可以省略该形参。为了避免遗漏参数或者参数传递的二义性,需在函数形参列表的末尾放置带默认值的形参,不要在非默认值的形参前放置。

      • 定义格式:

            func 函数名 (参数名1:参数类型, 参数名2:参数类型, sAge:String = "20") -> 函数返回类型 {
        
                函数体 .....
        
                return 返回值
            }
        
    • 在有定义默认值的情况下,当没有指定外部形参名称时,Swift 语言将为你定义的任何默认值形参提供一个自动外部形参名,这个自动外部形参名和本地形参名相同。

  • 3)可变数量形参:

    • 可变数量形参是指可接受零个或多个指定类型值的形参,可以用它来传递任意数量的输入参数。声明可变形参需要用到“...”,当参数传递进函数体后,参数在函数体内可以通过集合的形式访问。一个函数最多可以有一个可变参数,而且它必须出现在参数列表的最后。

      • 定义格式:

            func 函数名 (参数名1:参数类型, 参数名2:参数类型, numbers:Double...) -> 函数返回类型 {
        
                函数体 .....
        
                return 返回值
            }
        
  • 4)可变值形参:

    • Swift 语言函数的形参默认是常量,我们不能直接在函数体内部改变形参的值,也就是说函数的形参默认是值类型的。但是如果需要在函数体内部修改函数参数值,可以使用可变形参,要定义可变形参可以在参数名前使用 var 关键字。可变形参可以让你能够修改形参的值,它可以给函数体一个可修改的形参值副本,但这并不意味着可变形参就是引用类型的。

      • 定义格式:

            func 函数名 (var 参数名1:参数类型, var 参数名2:参数类型, ...) -> 函数返回类型 {
        
                函数体 .....
        
                return 返回值
            }
        
  • 5)引用类型形参:

    • 在实际的编码中,我们往往需要在函数体内部修改形参值,并同时作用到实参本身,从而省去增加返回值数量的步骤。这时可以把形参定义为 in-out 类型,要定义 in-out 类型的参数,需要在参数名前使用 inout 关键字。当把变量传递给 in-out 形参时,必须在变量前添加 “&” 符号,以表明他被函数内部修改的是它本身的引用。

      • 定义格式:

            func 函数名 (inout 参数名1:参数类型, var 参数名2:参数类型, ...) {
        
                函数体 .....
            }
        
            func *= (inout lhs: UInt, rhs: UInt)
        
    • 使用 in-out 参数的同时有几条规则需要注意:

      • 1、被标记为 inout 后不能将常量和字面量传递进函数。
      • 2、不能同时将参数标记为 var、let、inout。
      • 3、可变数量参数的参数不能标记为 inout。
      • 4、函数不能有默认值。
  • 6)函数类型:

    • 每一个函数都有特定的函数类型,函数类型通常由函数的形参类型和返回值类型组成。如果一个函数没有形参或返回值,那么这个函数的类型是 () -> () 。例如函数

          func addString(s1:string, s2:string, s2:string) -> String { } 的类型就是 (String, String, String) -> String 。
      
    • 可以在 Swift 语言中像使用其他任何类型一样的使用函数类型。例如可以定义一个函数常量或函数变量,并像一般数据类型指定初始值一样为他指定一个对应的函数。与其他类型一样,当你给函数赋一个变量或者常量时,你可以让 Swift 语言去推断函数的类型。

          // 指定函数类型
          var addSome:(String, String, String) -> String = addString
      
          // 推断函数类型
          let anotherAddSome = addString
      
    • 你也可以使用一个函数类型作为另一个函数的形参类型和返回值类型,使用方法和一般的数据类型相同。

  • 7)嵌套函数:

    • 在一个函数体中定义另外一个函数体就称为嵌套函数。嵌套的函数默认对外是隐藏的,但仍可以通过包裹他们的函数调用和使用它们。

      • 定义格式:

            func 函数名1 (参数名1:参数类型, 参数名2:参数类型 ...) -> 函数返回类型1 {
        
                func 函数名2 (参数名3:参数类型, 参数名4:参数类型 ...) -> 函数返回类型2 {
        
                函数体2 .....
        
                return 返回值2
                }
        
                函数体1 .....
        
                return 返回值1
            }
        

注释

单行注释:

// 这是一个注释

多行注释:

/* 这是一个,
多行注释 */

多行注释可以嵌套:

/* 这是第一个多行注释的开头
/* 这是第二个被嵌套的多行注释 */
这是第一个多行注释的结尾 */

6、断言

  • 对每次运行都会出现的错误通常不会过于苦恼,可以使用断点调试或者 “try-catch”之类的方式判断并修复它。但是一些偶发(甚至是无数次运行才会出现一次)的错误单靠断点之类的方式是很难排除掉的,为此,引入一个不是很常用的调试工具函数:assert(condition: Bool, message: String),assert 是单纯地触发断言即停止程序,不会让你有机会将可能出错的设计走过它这一关。

  • 在实际编码中,为了保证程序正常运行,只有在某些必要条件被满足的情况下才执行特定代码段,这种编程思想叫做防错性编程。

  • 在 Swift 语言中可以调用全局的 assert 函数来增加一个断言,这里的全局意思是你可以将断言放在你程序的任何一个地方。程序在执行到 assert 时会判断其中的逻辑条件表达式参数是否为 true。如果条件判断为 true,代码运行会继续进行。如果条件判断为 false,程序将终止。通常,在为程序加入并触发断言后,Xcode 会精确定位到异常代码段,并反馈异常信息等修改 bug 必须的调试信息。

  • 标准的断言格式:

        assert(condition: Bool, message: String) 
    
    • condition 判断条件,message 自定义调试信息,断言中的调试信息参数是可选的。
  • 定义:

        func assert(condition: @autoclosure () -> Bool, 
                    _ message: @autoclosure () -> String = default, 
                         file: StaticString = default, 
                         line: UWord = default)
    
        @inline(__always) func assertionFailure(_ message: @autoclosure () -> String = default, 
                                                     file: StaticString = default, 
                                                     line: UWord = default)
    
  • 使用:

        var usedate = -1
        usedate = 2
    
        // 当 usedate 大于 0 时,程序中断,进入断言函数打印调试信息
        assert(usedate <= 0, "超出试用期,不能启动程序!")
    
  • 系统在断言的源代码中加入了类似 “#if NDEBUG”这样的编译字,使其只能用于 debug 期,当你在发布 release 版本或者更新版的时候,编译器会使用一个编译字段将断言无效化,所以当你的产品在提交给用户之后还需要继续收集错误信息时,需使用其他方式。

  • 断言函数中用到的“@autoclosure”属性,使用这种属性的参数意味着我们可以在这个参数的位置传入一个表达式,这个表达式会被自动封包成一个闭包,这也正是其字面的意思:“自动闭包”。在 assert 函数中它起到的作用也是非常明显的,如果在这里我们使用的是普通的布尔型参数,那么在执行到 assert 函数时,就会先运算条件表达式的值,而使用“@autoclosure”属性后,程序会先在 assert 函数内判断 debug 期的编译字是否存在,如果存在才会运算条件表达式的值,当然,这时条件表达式已经被自动封装成了一个闭包。

  • 断言使用的几种场景:

    • 验证参数的合法性。
    • 将要使用一个对象,但是不确定其是否已经正确创建。
    • 数组或者其他集合类、字典等复杂数据类型下标没有处于安全范围导致可能会越界。
    • assert 函数的条件表达式参数最好一次只判断一个条件,因为如果判断多个条件,当断言被触发时,往往会无法直观的判断到底是哪一个条件不被满足。

类型别名

类型别名(type aliases)就是给现有类型定义另一个名字。

你可以使用typealias关键字像使用普通的赋值语句一样,将某个已经存在的类型赋值为新的名字。

typealias 是单一的,不能将整个泛型类型进行重命名。只有泛型类型的确定性得到保证后,我们才可以重命名。

7、闭包

  • 闭包 在 Swift 中非常有用。通俗的解释就是一个 Int 类型里存储着一个整数,一个 String 类型包含着一串字符,同样,闭包是一个包含着函数的类型。有了闭包,你就可以处理很多在一些古老的语言中不能处理的事情。这是因为闭包使用的多样性,比如你可以将闭包赋值给一个变量,你也可以将闭包作为一个函数的参数,你甚至可以将闭包作为一个函数的返回值。它的强大之处可见一斑。在 Swift 的很多文档教材中都说函数是“一等公民”,起初我还不是很理解“一等公民”是什么意思,但当我理解了闭包以及它的强大功能后,我恍然大悟、茅塞顿开、醍醐灌顶。原来闭包的这些特性就是“一等公民”的特性啊!

  • 闭包是功能性自包含模块,可以在代码中被传递和使用。一段程序代码通常由常量、变量和表达式组成,然后使用一对花括号“{}” 来表示闭合并包裹着这些代码,由这对花括号包裹着的代码块就是一个闭包。Swift 中的闭包与 C 和 Objective-C 中的 Block 以及其他一些编程语言中的 lambdas 比较相似。Block 和闭包的区别只是语法的不同而已,而且闭包的可读性比较强。

  • 闭包是引用类型,无论你将函数/闭包赋值给一个常量还是变量,实际上都是在将常量/变量设置为对应函数/闭包的引用,这也意味着如果你将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包。

  • 在 Swift 语言中有三种闭包形式:

    • 全局函数:是一个有名字但不会捕获任何值的闭包。
    • 嵌套函数:是一个有名字并可以捕获到其封闭函数域内的值的闭包。
    • 匿名闭包:闭包表达式是一个利用轻量级语法所写的,可以捕获其上下文中变量或常量值。
  • 1)函数形式:

        func myConpare(s1:String, s2:String) -> Bool {
    
            return s1 > s2
        }
    
        let namesArray:Array = ["Jill", "Tim", "Chris"]
    
        let names = namesArray.sort(myConpare)
    
  • 2)一般形式:

        { (parameters参数) -> returnType返回类型 in
    
            statements
        }
    
    • 可以使用常量、变量、inout、可变参数、元组类型作为闭包的参数,但不能在闭包参数中设置默认值,定义返回值和函数返回值的类型相同。

    • 闭包表达式中的 in 关键字表示闭包的参数和返回值类型定义已经完成,这些参数和返回值都将在下面的闭包函数体中得到处理。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1:String, s2:String) -> Bool in
      
              return s1 > s2
          }
      
  • 3)参数类型隐藏形式:

    • Swift 中有类型推断的特性,所以我们可以去掉参数类型。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) -> Bool in
      
              return s1 > s2
          }
      
  • 4)返回值类型隐藏形式:

    • Swift 中有类型推断的特性,所以我们可以去掉返回值类型。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) in
      
              return s1 > s2
          }
      
  • 5)return 隐藏形式:

    • 单行表达式的闭包可以通过隐藏关键字 return 来隐式地将单行表达式的结果作为返回值。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort { (s1, s2) in
      
              s1 > s2
          }
      
  • 6)参数名省略形式:

    • 闭包的使用非常的灵活,我们可以省略闭包参数列表中的参数的参数类型定义,被省略的参数类型会通过闭包函数的类型进行推断。同时,我们也可以在闭包函数体中通过使用闭包的参数名简写功能,直接使用 $0、$1、$2 等名字就可以引用的闭包参数值。如果同时省略了参数名和参数类型,那么 in 关键字也必须被省略,此时闭包表达式完全由闭包函数体构成。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort {
      
              $0 > $1
          }
      
  • 7)trailing 闭包形式:

    • 闭包可以做其他函数的参数,而且通常都是函数的最后一个参数。但是如果作为参数的这个闭包表达式非常长,那么很有可能会影响函数调用表达式的可读性,这个时候我们就应该使用 trailing 闭包。trailing 闭包和普通闭包的不同之处在于它是一个书写在函数参数括号之外(之后)的闭包表达式,函数会自动将其作为最后一个参数调用。

    • 当函数有且仅有一个参数,并该参数是闭包时,不但可以将闭包写在 () 外,还可以省略 ()。Swift 2.2 中可以不管参数的个数完全省略 ()。

          let namesArray:Array = ["Jill", "Tim", "Chris"]
      
          let names = namesArray.sort() {
      
              $0 > $1
          }
      
  • 8)闭包捕获:

    • 闭包可以在其定义的上下文中捕获常量或变量,即使定义这些常量或变量的原作用域已经不存在,仍然可以在闭包函数体内引用和修改这些常量或变量,这种机制被称为闭包捕获。比如:嵌套函数就可以捕获其父函数的参数以及定义的常量和变量,全局函数可以捕获其上下文中的常量或变量。

          func increment(amount: Int) -> (() -> Int) {
      
              var total = 0
      
              func incrementAmount() -> Int {
      
              // total 是外部函数体内的变量,这里是可以捕获到的
                  total  = amount
      
                  return total
              }
      
              // 返回的是一个嵌套函数(闭包)
              return incrementAmount
          }
      
          // 闭包是引用类型,所以 incrementByTen 声明为常量也可以修改 total
          let incrementByTen = increment(10)
          incrementByTen()    // return 10,incrementByTen 是一个闭包
      
          // 这里是没有改变对 increment 的引用,所以会保存之前的值
          incrementByTen()    // return 20
          incrementByTen()    // return 30
      
          let incrementByOne = increment(1)
          incrementByOne()    // return 1,incrementByOne 是一个闭包
          incrementByOne()    // return 2
          incrementByTen()    // return 40
          incrementByOne()    // return 3
      

基本运算符(Basic Operators)

赋值运算符 =

算术运算符,基本的四则算术运算:

  • 加法(
  • 减法(-
  • 乘法(*
  • 除法(/

求余运算符 a % b

自增 和自减 --

一元负号 -

一元正号

8、下标脚本

  • 下标脚本 允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义下标脚本使用 subscript 关键字,显式声明入参(一个或多个)和返回类型,每个输入参数的类型也没有限制,返回值可以是任何类型,并无限制。输入参数也可以使用可变参数,但使用输入/输出(in-out)参数或和给参数设置默认值都是不允许的。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的 getter 和 setter 方法。

  • 下标脚本就是对一个东西通过索引,快速取值的一种语法,例如数组的 a[0]。这就是一个下标脚本。通过索引 0 来快速取值。在 Swift 中,我们可以对类(Class)、结构体(structure)和枚举(enumeration)中自己定义下标脚本的语法。

  • 重点:

    • 下标脚本使用 subscript 关键字来定义。
    • 下标脚本使用 get、set 来定义读、写属性,并不需要 2 个属性都有,可以只读,并且读必须有。
    • 定义 set 属性时,传入的参数默认名称为 newValue。并且 newValue 的类型和 subscript 函数返回值相同。
  • 1) 下标脚本的使用 1

    • 下标脚本的定义

          struct myString {
      
              var str:String = ""
      
              subscript(start:Int, length:Int) -> String {
      
                  get {
                      return (str as NSString).substringWithRange(NSRange(location: start, length: length))
                  }
      
                  set {
      
                      str = newValue
                  }
              }
          }
      
    • 下标脚本的使用

          let str1 = myString(str: "hello world")
      
          let str2 = str1[2, 5]
      
          // 输出 hello world
          print(str1[0, 11])
      
          // 输出 llo w
          print(str2)
      
          var str3 = myString()
      
          // [0, 0] 参数无意义
          str3[0, 0] = "world"
      
          // 输出 world
          print(str3[0, 5])
      
  • 2) 下标脚本的使用 2

    • 下标脚本的定义

          class Student1 {
      
              var scores:[Int] = Array(count:5, repeatedValue:0)
      
              subscript(index:Int) -> Int {
      
                  get {
      
                      return scores[index];
                  }
      
                  set {
      
                      scores[index] = newValue
                  }
              }
      
              subscript(indexs:Int...) -> [Int] {
      
                  get {
                      var values:[Int] = Array()
      
                      for index in indexs {
                          values.append(scores[index])
                      }
                      return values
                  }
      
                  set {
                      var i = 0
      
                      for index in indexs {
      
                          scores[index] = newValue[i]
      
                          i  = 1
                      }
                  }
              }
          }
      
    • 下标脚本的使用

          let stu1 = Student1()
      
          stu1[0] = 1
          stu1[1] = 2
      
          // 输出 a[0]:1, a[1]:2
          print("a[0]:(stu1[0]), a[1]:(stu1[1])")
      
          let stu2 = Student1()
      
          stu2[1, 2, 3] = [5, 6, 7]
      
          // 输出 [0, 5, 6, 7, 0]
          print(stu2[0, 1, 2, 3, 4])
      

复合赋值

组合加运算 =

比较运算符

  • 等于(a == b
  • 不等于(a != b
  • 大于(a > b
  • 小于(a < b
  • 大于等于(a >= b
  • 小于等于(a <= b

三目运算符

原型是 问题 ? 答案1 : 答案2

空合运算符

空合运算符 a ?? b

空合并运算符是对以下代码的简短表达方法

a != nil ? a! : b

区间运算符

闭区间运算符 a...b

半开区间 a..<b

逻辑运算

  • 逻辑非(!a
  • 逻辑与(a && b
  • 逻辑或(a || b

字符串和字符

String 是有序的 Character 类型的值的集合。

字符串是值拷贝,而非引用。

集合类型 (Collection Types)

Swift 语言提供ArraysSetsDictionaries三种基本的集合类型用来存储集合数据。数组是有序数据的集,集合是无序无重复数据的集,字典是无序的键值对的集。

  1. 可以用 Array<T> 也可以用 [T] 这样的简单语法创建一个数组。

创建一个空数组

var someInts = [Int]()

创建一个带有默认值的数组

var threeDoubles = [Double](count: 3, repeatedValue:0.0)
  1. Swift中的Set类型被写为Set<T>, 这里的T表示Set中允许存储的类型,和数组不同的是,集合没有等价的简化形式。

  2. Swift 的字典使用Dictionary<Key, Value>定义,其中Key是字典中键的数据类型,Value是字典中对应于这些键所存储值的数据类型。也可以用[Key: Value]这样快捷的形式去创建一个字典类型。

控制流

for...in...for initialization; condition; increment { statements }

while condition { statements }repeat { statements } while condition

ifswitch

在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用break语句。这使得switch语句更安全、更易用,也避免了因忘记写break语句而产生的错误。

Swift 有四种控制转移语句。

  • continue
  • break
  • fallthrough
  • return
  • throw

强制解包:可以使用 if 语句来检测一个可选类型是否包含一个特定的值,如果一个可选类型确实包含一个值,在 if 语句中它将返回 true,否则返回 false。如果你已经检测确认该值存在,那么可以使用或者输出它,在输出的时候只需要在名称后面加上感叹号(!)即可,意思是告诉编译器:我已经检测好这个值了,可以使用它了。
强制解包,是一种语法,在其它地方也可以使用,并非仅限于此。

选择绑定:选择绑定帮助确定一个可选值是不是包含了一个值,如果包含,把该值转化成一个临时常量或者变量(语法上使用 let)。选择绑定可以用在 if 或 while 语句中,用来在可选类型外部检查是否有值并提取可能的值。(条件判断,并不一定需要是 Bool 类型!)

断言

断言会在运行时判断一个逻辑条件是否为true。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为true,代码运行会继续进行;如果条件判断为false,代码执行结束,你的应用被终止。

使用全局函数 assert 来使用断言调试,assert 函数接受一个布尔表达式和一个断言失败时显示的消息。

带标签的语句

label name: while condition { statements }

guard 提前退出

if语句一样,guard的执行取决于一个表达式的布尔值。我们可以使用guard语句来要求条件必须为真时,以执行guard语句后的代码。不同于if语句,一个guard语句总是有一个else分句,如果条件不为真则执行else分局中的代码。

guard condition else xxx 和其它语言里的 xxx unless condition 效果类似。

函数

没有参数名字的函数,复杂的带局部和外部参数名的函数。参数可以提供默认值,参数也可以既当做传入参数,也当做传出参数。

每个函数都有一种类型,包括函数的参数值类型和返回值类型。

函数可以有多个输入参数,写在圆括号中,用逗号分隔。

函数可以没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。

函数可以没有返回值。

函数的定义以 func 作为前缀,用返回箭头 ->后跟返回类型的名称的方式来表示返回类型。

严格上来说,没有定义返回类型的函数会返回特殊的值,叫 Void。它其实是一个空的元组(tuple),没有任何元素,可以写成()

Swift 里输入参数可以做得很复杂,支持泛型、函数等,记住一条不变法则:不论嵌套几层,输入参数始终要有 () 包裹着。

被调用时,一个函数的返回值可以被忽略。

你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。

函数参数都有一个外部参数名(external parameter name)和一个本地参数名(local parameter name).外部参数名用来标记传递给函数调用的参数,本地参数名在实现函数的时候使用。(对于 Ruby 使用者而言,这是新的概念)

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // function body goes here
    // firstParameterName and secondParameterName refer to
    // the argument values for the first and second parameters
}
someFunction(1, secondParameterName: 2)

一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名.所有参数需要有不同的本地参数名,但可以共享相同的外部参数名.

类似 Ruby 里传递哈希、或多参数,视觉上更好对应,自动匹配。只不过,对于 Swift 来说,感觉有点怪。

如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。(记得,以哈希的形式调用哈~)

func sayHello(to person: String, and anotherPerson: String) -> String {
    return "Hello (person) and (anotherPerson)!"
}

sayHello(to: "Bill", and: "Ted")
// prints "Hello Bill and Ted!"

如果你不想为第二个及后续的参数设置参数名,用一个下划线(_)代替一个明确地参数名。因为第一个参数默认忽略其外部参数名称,明确写下划线是多余的。(在这里,既然都忽略了,那干脆直接的、不使用此特性,不行吗)

你可以在函数体中为每个参数定义默认值(Deafult Values)。当默认值被定义后,调用这个函数时可以忽略这个参数。
将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。

一个可变参数(variadic parameter)可以接受零个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入(...)的方式来定义可变参数。
传入可变参数的值在函数体内当做这个类型的一个数组。

函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。

但是,有时候,如果函数中有传入参数的变量值副本将是很有用的。你可以通过指定一个或多个参数为变量参数,从而避免自己在函数中定义新的变量。变量参数不是常量,你可以在函数中把它当做新的可修改副本来使用。

通过在参数名前加关键字 var 来定义变量参数。

对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。

变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。

定义一个输入输出参数时,在参数定义前加 inout 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。

你只能将变量作为输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数前加&符,表示这个值可以被函数修改。(调用的时候,变量前加 & 表示传递的是引用)

输入输出参数是函数对函数体外产生影响的另一种方式。

每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。

使用函数类型

在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将函数赋值给它:

var mathFunction: (Int, Int) -> Int = addTwoInts

变量是 mathFunction 类型是函数 addTwoInts 而此函数的输入、输出类型分别是 (Int, Int)Int

函数类型作为参数类型

你可以用(Int, Int) -> Int这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现交由给函数的调用者。

下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:

func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: (mathFunction(a, b))")
}

printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"

被嵌套函数在调用时,函数参数为 in 前面的代码,函数体为 in 后面的代码。
被嵌套函数的类型是可预先知道的,并且定义调用同时进行。

函数类型作为返回类型

你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}

以第一个 -> 为分隔,输入参数类型为 (backwards: Bool),输出也就是返回类型为 (Int) -> Int

嵌套函数

全局函数,定义在全局域中。嵌套函数,定义在别的函数体中。

闭包

闭包采取如下三种形式之一:

  • 全局函数是一个有名字但不会捕获任何值的闭包
  • 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
  • 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包

sort(_:)方法需要传入两个参数:

  • 已知类型的数组
  • 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false

和 Ruby 里 map 等方法一样的,容易理解。

闭包表达式语法有如下一般形式:

{ (parameters) -> returnType in
    statements
}

parameters 表示输入参数,returnType 表示输出类型,in 是关键字表示开始执行内容,statements 表示执行内容。

根据上下文推断类型

实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。

也就是说,输入参数的类型和输出类型是可省的。

reversed = names.sort( { s1, s2 in return s1 > s2 } )

单表达式闭包隐式返回

单行表达式闭包可以通过隐藏return关键字来隐式返回单行表达式。

参数名称缩写

Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。

in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成。

运算符函数

reversed = names.sort(>)

省略到极致,真的能看懂吗?

尾随闭包

如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
    // 闭包主体部分
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
  // 闭包主体部分
}

捕获值

Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

闭包是引用类型

无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。

枚举

在 Swift 中,枚举类型是一等公民(first-class)。它们采用了很多传统上只被类(class)所支持的特征,例如计算型属性(computed properties),用于提供关于枚举当前值的附加信息,实例方法(instance methods),用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始的实现基础上扩展它们的功能;可以遵守协议(protocols)来提供标准的功能。

使用enum关键词来创建枚举并且把它们的整个定义放在一对大括号内:

enum SomeEnumeration {
  // enumeration definition goes here
}

里面的内容被称为 成员值

多个成员值可以出现在同一行上,用逗号隔开。

一旦一个变量,被赋值为一个枚举的成员值澳门新萄京官方网站,,你可以使用一个缩写语法(.)将其设置为另一个成员值

你可以使用switch语句匹配单个枚举值。

作为相关值的另一种选择,枚举成员可以被默认值赋值(语法上,和普通的赋值一样),其中这些原始值具有相同的类型(声明的时候要标注)。
你也可以在枚举类型开头加上indirect关键字来表示它的所有成员都是可递归的。

定义:

enum Result<T, U> {
  case Success(T)
  case Failure(U)
}

枚举,和类一样,可以当做普通变量的“类型”。

调用:

let aSuccess : Result<Int, String> = .Success(123)let aFailure : Result<Int, String> = .Failure("temperature too high")

注意:定义时对泛型的支持,调用时对泛型的指定。

类和结构体

类和结构体对比

Swift 中类和结构体有很多共同点。共同处在于:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义附属脚本用于访问值
  • 定义构造器用于生成初始化值
  • 通过扩展以增加默认实现的功能
  • 实现协议以提供某种标准功能

与结构体相比,类还有如下的附加功能:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 解构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用

定义

类和结构体有着类似的定义方式。我们通过关键字classstruct来分别表示类和结构体,并在一对大括号中定义它们的具体内容。

生成结构体和类实例的语法一样。

通过使用 .,你可以访问实例中所含有的属性。
你也可以使用点语法为属性变量赋值。

所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:

let vga = Resolution(width:640, height: 480)

以哈希的形式传参,创建对应的实例,很正常啊。

结构体和枚举是值类型

在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。

类是引用类型

与值类型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,操作的是引用,其并不是拷贝。因此,引用的是已存在的实例本身而不是其拷贝。

如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:

恒等运算符

  • 等价于 ( === )
  • 不等价于 ( !== )

指针

概念被弱化了,甚至可以说没有了。

绝大部分的自定义数据构造都应该是类,而非结构体。

Swift 中字符串(String),数组(Array)字典(Dictionary)类型均以结构体的形式实现。

属性

存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。

如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性。

延迟存储属性

第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 lazy 来标示一个延迟存储属性。

如果一个被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。

计算属性

除存储属性外,类、结构体和枚举可以定义计算属性。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。

如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称newValue

只读计算属性的声明可以去掉get关键字和花括号。

属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。

可以为属性添加如下的一个或全部观察器:

  • willSet在新的值被设置之前调用
  • didSet在新的值被设置之后立即调用

计算属性和属性观察器所描述的模式也可以用于全局变量局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。

类型属性

为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是类型属性

使用关键字static来定义类型属性。

跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。

类似“类变量”。

方法

实例方法是属于某个特定类、结构体或者枚举类型实例的方法。

实例方法的语法与函数完全一致。

和调用属性一样,用点语法(dot syntax)调用实例方法。

方法的局部参数名称和外部参数名称 - 和函数一样。

修改方法的外部参数名称。

1)你可以自己添加一个显式的外部名称或者用一个井号(#)作为第一个参数的前缀来把这个局部名称当作外部名称使用。

2)相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(_)作为该参数的显式外部名称,这样做将覆盖默认行为。

类型的每一个实例都有一个隐含属性叫做selfself完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。

使用self属性来区分参数名称和属性名称。
实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。

在实例方法中修改值类型

结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。

但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法。

要使用变异方法, 将关键字mutating 放到方法的func关键字之前就可以了。

在变异方法中给self赋值

变异方法能够赋给隐含属性self一个全新的实例。

枚举的变异方法可以把self设置为相同的枚举类型中不同的成员。

** 类型方法**

声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static

俗称的“类方法”。

下标脚本

下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。

与定义实例方法类似,定义下标脚本使用subscript关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:

subscript(index: Int) -> Int {
    get {
      // 返回与入参匹配的Int类型的值
    }

    set(newValue) {
      // 执行赋值操作
    }
}

newValue的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明newValue就算不写,在set代码块中依然可以使用默认的newValue这个变量来访问新赋的值。

下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。

一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是下标脚本的重载

继承

Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。

如果要重写某个特性,你需要在重写定义的前面加上override关键字。

在合适的地方,你可以通过使用super前缀来访问超类版本的方法,属性或下标脚本。

你可以通过把方法,属性或下标脚本标记为final来防止它们被重写,只需要在声明关键字前加上final特性即可。

构造过程

构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名。

你可以在定义构造器时提供构造参数,为其提供自定义构造所需值的类型和名字。构造器参数的功能和语法跟函数和方法参数相同。

Swift 会默认为每个构造器的参数自动生成一个跟内部名字相同的外部名,就相当于在每个构造参数之前加了一个哈希符号。(这是特性之一,也是和普通方法的区别之一)

如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(_)来显示描述它的外部名,以此重写上面所说的默认行为。

指定构造器和便利构造器

类的指定构造器的写法跟值类型简单构造器一样:

init(parameters) {
    statements
}

便利构造器也采用相同样式的写法,但需要在init关键字之前放置convenience关键字,并使用空格将它们俩分开。

convenience init(parameters) {
    statements
}

如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有必要的。
你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)

带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的默认参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和枚举类型成员所带的原始值匹配,则该构造器构造一个带此原始值的枚举成员,否则构造失败。

通常来说我们通过在init关键字后添加问号的方式来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!),该可失败构造器将会构建一个特定类型的隐式解析可选类型的对象。

你可以在 init?构造器中代理调用 init!构造器,反之亦然。
你也可以用 init?重写 init!,反之亦然。
你还可以用 init代理调用init!,但这会触发一个断言:是否 init! 构造器会触发构造失败?

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器:

class SomeClass {
    required init() {
        // 在这里添加该必要构造器的实现代码
    }
}

当子类重写基类的必要构造器时,必须在子类的构造器前同样添加required修饰符以确保当其它类继承该子类时,该构造器同为必要构造器。在重写基类的必要构造器时,不需要添加override修饰符:

class SomeSubclass: SomeClass {
    required init() {
        // 在这里添加子类必要构造器的实现代码
    }
}

如果某个存储型属性的默认值需要特别的定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。每当某个属性所属的新类型实例创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
注意闭包结尾的大括号后面接了一对空的小括号。

析构过程

析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字deinit来标示,类似于构造器要用init来标示。

在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数。

解决实例之间的循环强引用

主导语法,秒钟入门教程。Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。

弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。

和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。

捕获列表中的每一项都由一对元素组成,一个元素是weakunowned关键字,另一个元素是类实例的引用(如self)或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方。

可空链式调用

在链式调用过程中 ?! 的使用。

错误处理

在Swift中,错误用符合ErrorType协议的值表示。

Swift枚举特别适合把一系列相关的错误组合在一起,同时可以把一些相关的值和错误关联在一起。因此编译器会为实现ErrorType协议的Swift枚举类型自动实现相应合成。

通过在函数或方法声明的参数后面加上throws关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把throws关键字放在返回箭头(->)的前面。

在抛出函数体的任意一个地方,可以通过throw语句抛出错误。

当调用一个抛出函数的时候,在调用前面加上try。这个关键字表明函数可以抛出错误,而且在try后面代码将不会执行。

使用do-catch语句来就捕获和处理错误。

do {
    try function that throws
    statements
} catch pattern {
    statements
}

使用了 do 的其它语法:

do {
  let a = Animals.Troll
  ...
}

在运行时,有几种情况抛出函数事实上是不会抛出错误的。在这几种情况下,你可以用forced-try表达式来调用抛出函数或方法,即使用try!来代替try

使用defer语句来在执行一系列的语句。这样不管有没有错误发生,都可以执行一些必要的收尾操作。

类型转换

类型转换在 Swift 中使用 isas 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。

用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false

某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as?as!)

因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) as? 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 as! 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。

转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。

Swift为不确定类型提供了两种特殊类型别名:

  • AnyObject可以代表任何class类型的实例。
  • Any可以表示任何类型,包括方法类型(function types)。

嵌套类型

在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名。

扩展

扩展就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能(functionality)。

Swift 中的扩展可以:

  • 添加计算型属性和计算型静态属性
  • 定义实例方法和类型方法
  • 提供新的构造器
  • 定义下标
  • 定义和使用新的嵌套类型
  • 使一个已有类型符合某个协议

声明一个扩展使用关键字extension

1)扩展可以向已有类型添加计算型实例属性和计算型类型属性。
2)扩展可以向已有类型添加新的构造器。
3)扩展可以向已有类型添加新的实例方法和类型方法。
4)通过扩展添加的实例方法也可以修改该实例本身。
5)扩展可以向一个已有类型添加新下标。
6)扩展可以向已有的类、结构体和枚举添加新的嵌套类型。

协议

协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为遵循(conform)这个协议。

协议的定义方式与类,结构体,枚举的定义非常相似。

要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。

如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

1、对属性的规定

协议可以规定其遵循者提供特定名称和类型的实例属性(instance property)类属性(type property),而不指定是存储型属性(stored property)还是计算型属性(calculate property)。此外还必须指明是只读的还是可读可写的。

** 2、对方法的规定**

协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。

3、对Mutating方法的规定

有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

和 Ruby 里某些方法带 ! 是一个道理,因为调用这些方法后,会改变对象本身。或者说,具有破坏性。

4、对构造器的规定

协议可以要求它的遵循者实现指定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体。

协议类型

尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。

协议可以像其他普通类型一样使用,使用场景:

  • 作为函数、方法或构造器中的参数类型或返回值类型
  • 作为常量、变量或属性的类型
  • 作为数组、字典或其他容器中的元素类型

协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔。

类专属协议

你可以在协议的继承列表中,通过添加 class 关键字, 限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。

协议合成

有时候需要同时遵循多个协议。你可以将多个协议采用protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成(protocol composition)。你可以在<>中罗列任意多个你想要遵循的协议,以逗号分隔。

protocol<SomeProtocol, AnotherProtocol> 前面的 protocol 是关键字,<> 里面是各个协议的名字。
协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。

检验协议的一致性

  • is操作符用来检查实例是否遵循了某个协议
  • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
  • as用以强制向下转型,如果强转失败,会引起运行时错误。

协议可以含有可选成员,其遵循者可以选择是否实现这些成员。在协议中使用optional关键字作为前缀来定义可选成员。

为协议扩展添加限制条件

在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用where关键字来描述限制情况。

泛型

函数功能都是相同的,唯一不同之处就在于传入的变量类型不同。

泛型函数名后面跟着的占位类型名字(T)是用尖括号括起来的(<T>)。
你可支持多个类型参数,命名在尖括号中,用逗号分开。

当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。

泛型函数使用了占位类型(通常此情况下用字母T来表示)来代替实际类型(如IntStringDouble)。

另外一个不同之处在于这个泛型函数名后面跟着的占位类型(T)是用尖括号括起来的(<T>)。这个尖括号告诉 Swift 那个T是函数所定义的一个类型。但因为T是一个占位类型,Swift 不会去查找命名为T的实际类型。

类型约束

你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。

关联类型

当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型作为协议的一部分,给定了类型的一个占位名(或别名)。作用于关联类型上实际类型在协议被实现前是不需要指定的。关联类型被指定为typealias关键字。

typealias ItemType。这个协议不会定义ItemType是什么的别名,这个信息将由任何遵循协议的类型来提供。

这里的 typealias 和类型别名,意义是不一样的。遵行此协议的“类”,总得有对应的“类型”吧。但是这个“类型”具体是什么,又不能确定,那么先用 ItemType 代替。(因为协议里面要用了,不能不提供;仅/只能提供一次)

Where 语句

对关联类型定义约束是非常有用的。你可以在参数列表中通过where语句定义参数的约束。一个where语句能够使一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可以写一个where语句,紧跟在在类型参数列表后面,where语句后跟一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型间的等价(equality)关系。

访问控制

Swift 中的访问控制模型基于模块和源文件这两个概念。

模块指的是以独立单元构建和发布的FrameworkApplication。在Swift 中的一个模块可以使用import关键字引入另外一个模块。

源文件指的是 Swift 中的Swift File,就是编写 Swift 源代码的文件,它通常属于一个模块。尽管一般我们将不同的 分别定义在不同的源文件中,但是同一个源文件可以包含多个函数 的定义。

Swift 为代码中的实体提供了三种不同的访问级别。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。

  • public:可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。通常情况下,Framework 中的某个接口是可以被任何人使用时,你可以将其设置为public级别。
  • internal:可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或Framework作为内部结构使用时,你可以将其设置为internal级别。
  • private:只能在当前源文件中使用的实体,称为私有实体。使用private级别,可以用作隐藏某些功能的实现细节。

public为最高级访问级别,private为最低级访问级别。

如果你不为代码中的所有实体定义显式访问级别,那么它们默认为internal级别。

单元测试目标的访问级别

默认情况下只有public级别的实体才可以被其他模块访问。然而,如果在引入一个生产模块时使用@testable注解,然后用带测试的方式编译这个生产模块,单元测试目标就可以访问所有internal级别的实体。

高级运算符

Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(& )。所有的这些溢出运算符都是以 & 开头的。

  • 溢出加法 &
  • 溢出减法 &-
  • 溢出乘法 &*

位运算符

1)按位取反运算符(~)
2)按位与运算符(&)
3)按位或运算符(|)
4)按位异或运算符(^)
5)按位左移运算符(<<)和按位右移运算符(>>)

类和结构可以为现有的操作符提供自定义的实现,这通常被称为运算符重载(overloading)。

类与结构体也能提供标准单目运算符(unary operators)的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(prefix)的(比如 -a),而当它出现在操作目标之后时,它就是后缀(postfix)的(比如 i )。

要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 func 关键字之前指定 prefix 或者 postfix 限定符。

复合赋值运算符(Compound assignment operators)将赋值运算符(=)与其它运算符进行结合。比如,将加法与赋值结合成加法赋值运算符( =)。

还可以将赋值与 prefixpostfix 限定符结合起来。

不能对默认的赋值运算符(=主导语法,秒钟入门教程。)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符 a ? b : c 进行重载。

自定义的类和结构体没有对等价操作符(equivalence operators)进行默认实现,等价操作符通常被称为“相等”操作符(==)与“不等”操作符(!=)。

新的运算符要在全局作用域内,使用 operator 关键字进行声明,同时还要指定 prefixinfix 或者 postfix 限定符。

自定义中缀运算符的优先级和结合性

结合性(associativity)可取的值有leftrightnone。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的操作数进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的操作数进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。

结合性(associativity)的默认值是 none,优先级(precedence)如果没有指定,则默认为 100

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:主导语法,秒钟入门教程

关键词: