swift_swiftyjson - CSDN
swift 订阅
Swift,苹果于2014年WWDC苹果开发者大会发布的新开发语言,可与Objective-C共同运行于macOS和iOS平台,用于搭建基于苹果平台的应用程序。Swift是一款易学易用的编程语言,而且它还是第一套具有与脚本语言同样的表现力和趣味性的系统编程语言。Swift的设计以安全为出发点,以避免各种常见的编程错误类别。 [1]  2015年12月4日,苹果公司宣布其Swift编程语言开放源代码。长600多页的The Swift Programming Language [2]  可以在线免费下载。 展开全文
Swift,苹果于2014年WWDC苹果开发者大会发布的新开发语言,可与Objective-C共同运行于macOS和iOS平台,用于搭建基于苹果平台的应用程序。Swift是一款易学易用的编程语言,而且它还是第一套具有与脚本语言同样的表现力和趣味性的系统编程语言。Swift的设计以安全为出发点,以避免各种常见的编程错误类别。 [1]  2015年12月4日,苹果公司宣布其Swift编程语言开放源代码。长600多页的The Swift Programming Language [2]  可以在线免费下载。
信息
源    于
Objective-C
外文名
Swift
发布时间
2014年6月2日
发行组织
Apple
中文名
雨燕
参考语言
C,JavaScript,Python,Java
系    统
macOS,iOS,linux
SWIFT发展历史
2014-6-3 Swift于WWDC苹果开发者大会发布。 发布(6张) 2014-6-4 《Swift中文版》翻译组在github上进行协同翻译 [3]  。此项目开始不到一周就获得了1067个star。该项目的发起人是北航的大三学生 [4]  。2014-6-12《Swift中文版》第一版发布 [5]  。2015年12月4日,苹果公司宣布其Swift编程语言开放源代码 [3]  。长600多页的The Swift Programming Language [2]  可以在线免费下载。同时可以在苹果官方Github下载 [3]  。2020年最新一期的编程语言排行榜显示,Swift从第15名上升至第9名。 [6] 
收起全文
精华内容
参与话题
  • 从零开始学Swift——语法篇 第一季

    万人学习 2018-10-22 21:38:04
    本视频主要介绍Swift程序开发环境、Swift 语法基础、运算符、Swift原生数据类型、Swift原生字符串、控制语句。本视频是基 于Swift2.x版本录制,与图书《从零开始学Swift》语法篇同步讲解。是学习Swift视频,进行iOS...
  • Swift5语言入门实例教程

    千人学习 2019-12-05 11:06:29
    包含常量与变量、基本数据类型,以及Swift语言中的新数据类型—元组型和可选型的内容。同时还讲解了运算符和表达式、流程控制语句、字符和字符串、集合类型、函数和闭包。此外,Swift面向对象的枚举、结构体、类,...
  • Swift编程语言(Swift 5)

    2020-07-29 14:19:59
    介绍: 《Swift 编程语言》是苹果官方对 Swift 语言做的权威指南,很遗憾苹果公司并没有进行多语言支持。所以我独立发起了这个手册的翻译工作——与其他现存翻译不同的是:它同步更新苹果官方的 Swift 开发者预览版 ...
  • Swift快速入门

    2020-09-08 06:36:46
    Swift中规定:在定义一个标识符时必须明确说明该标识符是一个常量还是变量 1、使用 let 定义常量,定义之后不可以修改 let number = 10 // 常量不可以修改 number = 20 ❌ 2、使用 var 定义变量,定义之后可以...

    一、常量&变量

    在Swift中规定:在定义一个标识符时必须明确说明该标识符是一个常量还是变量

    1、使用 let 定义常量,定义之后不可以修改

    let number = 10
    // 常量不可以修改
    number = 20 ❌

    2、使用 var 定义变量,定义之后可以修改

    var number = 10
    
    number = 20 

    ⚠️编译器会根据给变量或常量所赋的值,推断它的数据类型。所以这里系统会认为number是Int类型的

    ⚠️ swift中输出语句:print函数

    print("Hello Swift")

    二、数据类型

    Swift中的数据类型也有:整型/浮点型/字符串/对象类型/结构体类型等等

    ⚠️ Swift是强类型语言

    // 定义变量时没有指定明确的类型,但是因为赋值给i一个20.20为整型.因此i为整型
    var i = 20
    // 如果之后赋值给i一个浮点型数值,则会报错
    // i = 30.5 ❌

    如果定义一个标识符时有直接进行赋值,那么标识符后面的类型可以省略

    如果想要指定常量或变量的数据类型

    var number:Float = 10
    var name:String = "花花"

    Swift中的基本运算

    ⚠️ Swift中在进行基本运算时必须保证类型一致,否则会出错

    ⚠️ 值不会隐式转换,需要显示转换成其他数据类型

    let a = 10
    let b = 3.14
    
    // 错误写法
    // let c = a + b ❌
    // let c = a * b ❌
    
    // 正确写法
    let c = Double(a) + b
    let d = a + Int(b)

    三、字符串

    字符串的使用

    1、遍历字符串

    // 字符串遍历
    var str = "Hello, Swift"
    for c in str {
        print(c)
    }

    2、字符串拼接

    let str1 = "Hello"
    let str2 = "Swift"
    let str3 = str1 + str2

    3、字符串插值

    let age = 18
    let result = "My name is Huahua, age is \(age)"

    4、字符串的格式化

    let str = String(format: "%@=%@", key, value)

    5、使用2对3引号可以表示多行字符串

    let string = """
    Hello Swift!
    Hello Swift!
    Hello Swift!
    """

    ⚠️引号所在行,引号后不能有其他元素

    四、数组

    数组是一串有序的由相同类型元素构成的集合,数组中的集合元素是有序的,可以重复出现

    ⚠️ 声明数组

    // let代表不可变数组
    let array:Array<String>
    
    // var代表可变数组
    var array: [String]

    ⚠️ 初始化数组

    // 定义一个可变数组,必须初始化才能使用
    var array1 : [String] = [String]()
    
    // 定义一个不可变数组
    let array2 : [Any] = ["花花", 18]
    
    // 定义时直接初始化
    var array = ["a", "b", "c"]
    
    // 先定义,后初始化
    var array : Array<String>
    array = ["a", "b", "c"]

    2、对数组的基本操作

    // 添加数据
    array.append("abc")
    
    // 删除元素
    array.removeFirst()
    
    // 修改元素
    array[0] = "asd"
    
    // 取值
    array[1]
    
    // 数组合并
    // 注意:只有相同类型的数组才能合并
    var array1 = ["a", "b","c"]
    var array2 = ["d", "e"]
    var array3 = array1 + array2

    3、数组的遍历

    // 遍历数组
    for i in 0..<array.count {
        print(array[i])
    }
    
    // forin方式
    for item in array {
        print(item)
    }
    
    // 设置遍历的区间
    for item in array[0..<2] {
        print(item)
    }

    五、字典

    字典是由两部分集合构成的,一个是键(key)集合,一个是值(value)集合。键集合是不能有重复元素的,值集合可以重复,键和值是成对出现的。

    1、字典的初始化

    // 定义一个可变字典
    var dict1 : [String : Any] = [String : Any]()
    
    // 定义一个不可变字典
    let dict2 = ["name" : "花花", "age" : 18]

    ⚠️ 声明字典

    var dict1: Dictionary<Int, String>
    var dict2: [Int: String]

    2、字典的基本操作

    // 添加数据
    dict["height"] = 1.88
    
    // 删除字段
    dict.removeValueForKey("height")
    
    // 修改字典
    dict["name"] = "花花"
    
    // 查询字典
    dict["name"]
    
    // 字典的合并
    var dict1 = ["name" : "花花", "age" : 18]
    var dict2 = ["height" : 1.8]
    
    // 字典不可以直接相加合并
    for (key, value) in dict2 {
        dict1[key] = value
    }

    3、字典的遍历

    // 遍历字典中所有的值
    for value in dict.values {
        print(value)
    }
    
    // 遍历字典中所有的键
    for key in dict.keys {
        print(key)
    }
    
    // 遍历所有的键值对
    for (key, value) in dict {
        print(key)
        print(value)
    }

    六、元祖

    元祖是一种数据结构,类似于数组或字典,用于定义一组数据

    1、定义元祖

    ("1001", "花花", 18, 90)
    (id:"1001", name:"张三", age:18, score:90)

    2、元祖的简单使用

    // 元祖:HTTP错误
    // 写法一:
    let error = (404, "Not Found")
    print(error.0)
    print(error.1)
    
    // 写法二:
    let error = (errorCode : 404, errorInfo : "Not Found")
    print(error.errorCode)
    print(error.errorInfo)
    
    // 写法三:
    let (errorCode, errorIno) = (404, "Not Found")
    print(errorCode)
    print(errorIno)

    七、可选类型(Optional)

    在swift开发中,nil也是一个特殊的类型。

    1、可选类型的定义

    // 写法一:定义可选类型
    let string : Optional<String> = nil
    
    // 写法二:定义可选类型,语法糖(常用)
    let string : String? = nil
    
    // 错误写法
    // let string : String = nil ❌

    2、可选类型的使用

    // 定义可选类型
    var string : String? = nil
    
    // 给可选类型赋值
    string = "Hello world"
    
    // 打印结果
    print(string)
    
    // 结果:Optional("Hello world")
    // 因为打印出来的是可选类型,所有会带Optional

    3、解包

    解包就是取出可选类型的真实值

    print(string!) // 结果:Hello world
    
    ⚠️注意:如果可选类型为nil,强制取出其中的值(解包),程序会崩溃
    
    string = nil
    print(string!) // 报错 
    
    // 正确写法:
    if string != nil {
        print(string!)
    }
    
    // 简单写法:为了让在if语句中可以方便使用string
    if var str = string {
        print(str)
    }

    4、可选类型使用实例

    // 通过该方法创建的URL,可能有值,也可能没有值
    
    // 正确写法:使用可选类型来接收
    let url : URL? = URL(string: "http://www.baidu.com")
    
    // 错误写法:如果返回值是nil时,就不能接收了
    let url : URL = URL(string: "http://www.baidu.com")
    
    // 通过url来创建request对象
    if let tempUrl = url {
        let request = NSURLRequest(URL: tempUrl)
    }

    八、函数

    函数格式:

    func 函数名(参数列表) -> 返回值类型 {
        代码块
        return 返回值
    }
    • func是关键字,多个参数列表之间可以用逗号(,)分隔,也可以没有参数
    • 使用箭头“->”指向返回值类型
    • 如果函数没有返回值,返回值为Void.“-> 返回值类型”部分可以省略

    1、常见函数类型

    <1 没有参数,没有返回值

    func test() -> Void {
        print("Hello Swift")
    }
    
    // 调用函数
    test()
    
    // 简单写法
    // 如果没有返回值,后面的内容可以都不写
    func test2() {
       print("Hello Swift")
    }

    <2 有参数,没返回值

    func test(name : String) {
        print("Hello\(name)")
    }
    test(name: "Swift")

    <3 没参数,有返回值

    func getSth() -> String {
        return "花花"
    }
    var str = getSth()
    print(str)

    <4 有参数,有返回值

    func sum(num1 : Int, num2 : Int) -> Int {
        return num1 + num2
    }
    var result = sum(num1: 20, num2: 30)
    print(result)

    2、外部参数&内部参数

    在函数外面可以看到的参数,就是外部参数。在函数内部可以看到的参数,就是内部参数。

    ⚠️ 如果不想要外部参数,可以在参数名称前加_

    // number1、number2和number3是外部参数的名称
    
    func mutiple(number1 num1 : Int, number2 num2 : Int, number3 num3 : Int) -> Int {
        return num1 * num2 * num3
    }
    var result1 = mutiple(number1: 20, number2: 4, number3: 5)

    3、默认参数

    某些情况,如果没有传入具体的参数,可以使用默认参数

    func makecoffee(type :String = "卡布奇诺") -> String {
        return "制作一杯\(type)咖啡。"
    }
    
    let coffee1 = makecoffee("拿铁")
    let coffee2 = makecoffee()

    4、可变参数

    swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数(必须具有相同的类型),我们可以通过在参数类型名后面加入(...)的方式来指示这是可变参数

    func sum(numbers:Double...) -> Double {
        var total: Double = 0
        for number in numbers {
            total += number
        }
        return total
    }
    
    sum(100.0, 20, 50)

    5、引用类型

    默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址

    inout关键字

    func swap(a : inout Int, b : inout Int) {
       let temp = a
       a = b
       b = temp
    }
    
    var a = 10
    var b = 20
    swap(a: &a, b: &b)
    print("a:\(a), b:\(b)")

    6、方法重载

    方法重载:方法名称相同,但是参数不同

    func mutiple(_ num1: Int, _ num2 :Int) -> Int {
        return num1 * num2
    }
    
    var result2 = mutiple(20, 20)

    九、 闭包

    闭包是一个特殊函数

    1、定义闭包

    类型:(形参列表)->(返回值)

    技巧:定义闭包类型时,直接写()->().再填充参数和返回值

    {
        (形参) -> 返回值类型 in
        // 执行代码
    }

    2、闭包的使用

    // 定义网络请求类
    class HttpTool: NSObject {
        func loadRequest(callBack: ()->()){
            print("加载数据..")
            callBack()
        }
    }
    
    // 网络请求
    let httpTool = HttpTool()
    httpTool.loadRequest ({ () -> () in
        print("加载完成")
    })   

    3、闭包的简写

    • 如果闭包没有参数,没有返回值.in和in之前的内容可以省略
    httpTool.loadRequest({
        print("加载完成")
    })

    ⚠️ 尾随闭包

    • 尾随闭包写法
      • 如果闭包是函数的最后一个参数,则可以将闭包写在()后面
      • 如果函数只有一个参数,并且这个参数是闭包,那么()可以不写
    httpTool.loadRequest() {
        print("加载完成")
    }
    // 开发中建议该写法
    httpTool.loadRequest {
        print("加载完成")
    }

    4、闭包的循环使用

    • 如果在HttpTool中有对闭包进行强引用,则会形成循环引用
    class HttpTool: NSObject {
        // 定义属性,强引用传入的闭包
        var callBack : (()->())?
    
        func loadRequest(callBack : ()->()){
            callBack()
            self.callBack = callBack
        }
    }
    • swift中解决循环引用的方式
    // [weak self] () -> () in
    httpTool.loadRequest { [weak self] in
        self.view.backgroundColor = UIColor.redColor()
    }

    十、 懒加载

    定义:用到时候才加载

    懒加载的本质是,在第一次使用的时候执行闭包,将闭包的返回值赋值给属性

    • lazy的作用是只会赋值一次
    lazy var array : [String] = {
        () -> [String] in
        return ["a", "b", "c"]
    }()

    十一、类

    Swift是一门面向对象开发的语言,面向对象的基础是类

    1、类的定义

    class 类名 : SuperClass {
        // 定义属性和方法
    }

    ⚠️ 定义的类,可以没有父类.那么该类是 rootClass

    2、类的属性

    • Swift中类的属性有多种
      • 存储属性:存储实例的常量和变量
      • 计算属性:通过某种方式计算出来的属性
      • 类属性:与整个类自身相关的属性

    存储属性

    • 存储属性是最简单的属性,它作为类实例的一部分,用于存储常量和变量
    • 可以给存储属性提供一个默认值,也可以在初始化方法中对其进行初始化
    class Student : NSObject {
        // 存储属性
        var age : Int = 0
        var name : String?
        var englishScore : Double = 0.0
        var mathScore : Double = 0.0
    }
    
    // 创建学生对象
    let stu = Student()
    // 给存储属性赋值
    stu.age = 10
    stu.name = "花花"
    stu.englishScore = 80.0
    stu.mathScore = 90.0

    计算属性

    • 计算属性并不存储实际的值,而是提供一个getter和一个可选的setter来间接获取和设置其它属性
    • 存储属性一般只提供getter方法
    • 如果只提供getter,而不提供setter,则该计算属性为只读属性,并且可以省略get{}
    class Student : NSObject {
        // 存储属性
        var age : Int = 0
        var name : String?
        var englishScore : Double = 0.0
        var mathScore : Double = 0.0
        
        // 计算属性
        var averageScore : Double {
            get {
                return (englishScore + mathScore) / 2
            }
            // newValue是系统分配的变量名,内部存储着新值
            set {
                self.averageScore = newValue
            }
        }
    }

    类属性

    • 类属性是与类相关联的,而不是与类的实例相关联
    • 类属性使用static来修饰
    class Student : NSObject {
        // 存储属性
        var age : Int = 0
        var name : String?
    
        var englishScore : Double = 0.0
        var mathScore : Double = 0.0
    
        // 计算属性
        var averageScore : Double {
            get {
                return (englishScore + mathScore) / 2
            }
    
            // 没有意义.newValue是系统分配的变量名,内部存储着新值
            set {
                self.averageScore = newValue
            }
        }
    
        // 类属性
        static var corseCount : Int = 0
    }
    
    // 设置类属性的值
    Student.corseCount = 3

    3、监听属性的改变

    Swift中可以通过属性观察者来监听和响应属性值的变化

    • 通常是监听存储属性和类属性的改变.(对于计算属性,我们不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化)

    • 我们通过设置以下观察方法来定义观察者

      • willSet:在属性值被存储之前设置。此时新属性值作为一个常量参数被传入。该参数名默认为newValue,我们可以自己定义该参数名
      • didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValue
    class Person : NSObject {
        var name : String? {
            // 可以给newValue自定义名称
            willSet (){ 
                // 属性即将改变,还未改变时会调用的方法
                // 在该方法中有一个默认的系统属性newValue,用于存储新值
                print(newValue)
            }
            didSet (oldValue) {
                // 属性值已经改变了,会调用的方法
                // 在该方法中有一个默认的系统属性oldValue,用于存储旧值
                print(oldValue)
            }
        }
        var age : Int = 0
        var height : Double = 0.0
    }
    
    let p : Person = Person()
    
    // 在赋值时,监听该属性的改变
    // 在OC中是通过重写set方法
    // 在swift中,可以给属性添加监听器
    p.name = "花花"

    十二、类的构造函数

    • 创建一个类时,必然会调用一个构造函数
    • 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。
    • 如果是继承自NSObject,可以对父类的构造函数进行重写

    1、构造函数的基本使用

    class Person: NSObject {
        var name : String = ""
        var age : Int = 0
    
        // 重写了NSObject(父类)的构造方法
        override init() {
            name = ""
            age = 0
        }
    }
    
    // 创建一个Person对象
    let p = Person()

    2、初始化时给属性赋值

    很多时候,我们在创建一个对象时就会给属性赋值,可以自定义构造函数 ⚠️ 如果自定义了构造函数,会覆盖init()方法.即不在有默认的构造函数

    class Person: NSObject {
        var name : String?
        var age : Int = 0
    
        // 自定义构造函数,会覆盖init()函数
        init(name : String, age : Int) {
            self.name = name
            self.age = age
        }
    }
    
    // 创建一个Person对象
    let p = Person(name: "花花", age: 18)

    十三、Swift逻辑分支

    通过分支语句可以控制程序的执行流程

    • 在Swift的判断句中必须有明确的真假 false/true

    1、if语句

    let a = 10
    
    // 错误写法:❌
    //if a {
    //    print("a")
    //}
    
    // 正确写法
    if a > 9 {
        print(a)
    }

    这个是可选类型,因为只有声明成可选类型后,才可以判断是否为空

    let view : UIView? = UIView()
    
    // 判断如果view有值,则设置背景颜色
    // 错误写法 ❌
    //if view {
    //    view.backgroundColor = UIColor.redColor()
    //}
    
    if view != nil {
        view!.backgroundColor = UIColor.redColor()
    }

    2、if/else if/else 语句

    let score = 87
    
    if score < 60 {
        print("不及格")
    } else if score <= 70 {
        print("及格")
    } else if score <= 80 {
        print("良好")
    } else if score <= 90 {
        print("优秀")
    } else {
        print("完美")
    }

    3、三目运算符

    var a = 10
    var b = 50
    
    var result = a > b ? a : b
    print(result)

    4、guard语句

    guard是Swift2.0新增的语法,它设计的目的是提高程序的可读性

    • guard语法:
      • 当条件表达式为true时候跳过else语句中的内容,执行后面的代码
      • 条件表达式为false时候执行else语句中的内容,跳转语句一般是return、break、continue和throw
    guard 条件表达式 else {
        // do sth..
        return
    }
    do sth...

    使用实例:

    var age = 18
    
    func online(age : Int) -> Void {
        guard age >= 18 else {
            print("回家去")
            return
        }
    
        print("可以上网")
    }
    
    online(age)

    5、switch分支

    switch后可以不跟(),case后可以不跟break(默认会有break)

    let sex = 0
    
    switch sex {
    case 0 :
        print("男")
    case 1 :
        print("女")
    default :
        print("其他")
    }

    如果希望出现之前的case穿透,则可以使用关键字fallthrough

    let sex = 0
    
    switch sex {
    case 0:
        fallthrough
    case 1:
        print("正常人")
    default:
        print("其他")
    }

    swift支持多种数据类型

    • 浮点型的switch判断
    let f = 3.14
    switch f {
    case 3.14:
        print("π")
    default:
        print("not π")
    }
    • 字符串类型
    let opration = "+"
    
    switch opration {
        case "+":
            print("+")
        case "-":
            print("-")
        case "*":
            print("*")
        case "/":
            print("/")
    default:
        break
    }
    • switch支持区间判断
    let score = 88
    
    switch score {
    case 0..<60:
        print("不及格")
    case 60..<80:
        print("及格")
    case 80..<90:
        print("良好")
    case 90..<100:
        print("优秀")
    default:
        print("满分")
    }

    十四、循环

    1、for循环

    for i in 0..<10 {
        print(i)
    }
    
    for i in 0...10 {
        print(i)
    }

    2、while循环

    var a = 0
    while a < 10 {
        a++
    }

    3、repeat while 循环

    let b = 0
    repeat {
        print(b)
        b++
    } while b < 20

     

     

    展开全文
  • 最近,除了N多的基于Swift的服务端开发框架,笔者不由深思,到底该这么评价Swift呢?不可否认,在iOS的开发领域,Swift是比OJC拥有着优势,那么在通用语言这个层次上比较时,它又如何呢?Apple 在推出 Swift 时就将...

    一  从语法角度,他的优势点

    最近,除了N多的基于Swift的服务端开发框架,笔者不由深思,到底该这么评价Swift呢?不可否认,在iOS的开发领域,Swift是比OJC拥有着优势,那么在通用语言这个层次上比较时,它又如何呢?Apple 在推出 Swift 时就将其冠以先进,安全和高效的新一代编程语言之名。前两点在 Swift 的语法和语言特性中已经表现得淋漓尽致:像是尾随闭包,枚举关联值,可选值和强制的类型安全等都是 Swift 显而易见的优点。

    1. Comparison

      oc java c# c++

    swift

    python
    值对象 没有 没有
    指针(缺陷) 没有 没有 没有 没有
    内存管理 引用计数 垃圾回收 垃圾回收 智能指针(缺陷) 引用计数 垃圾回收
    多返回值 没有 没有 没有 没有
    脚本语言特性 没有 没有 没有 没有
    移动端支持 ios android 游戏 游戏 ios 较少(缺陷)
                 

    近来Swift与Rust都挺好的,一个背靠Apple,一个是Mozilla的亲儿子。不可否认这二者都是工程领域的集大成者,不过笔者认为Swift是会比D或者Rust具有更大的可用性与吸引力,当然,他们的瞄准的目标点也不一样。D与Rust适合于那些长期使用C++并且已经适应了要去掌握N多的语法与概念的,但是想要使用些更加清晰明了与安全的语言。这类型的开发者往往从事着类似于游戏引擎、编译器、加解密库、HTML渲染引擎等等类似的工作。

    Swift呢,更多意义上是一门面向于应用编程的语言,它很容易上手,在某些方面它应该与Go、Java、Python以及C#相提并论。不过Swift比这些会更容易开始学习,它的helloworld只需要一行代码就好了,并且它是支持函数的(不像Java那样完全的OO)。你不需要学习任何的类与对象的知识就可以开始撰写简易的Swift的代码。基于此,Swift是更合适用作一种教学语言的,它还有像脚本语言一样的交互环境,也就是REPL以及Xcode本身提供的PlayGround。

    综上所述,Swift拥有着被广泛使用以及当做第一学习语言的潜质。并且,Swift并不是像PHP那样的语法特性较少的语言,它拥有足够的深度来满足Rust或者D这样的用户的需求。与Go相比的话,Go背靠Google,也是个非常容易上手的语言,并且号称自带并发。Swift在语法层次上会更加高级,并且Swift并没有使用GC机制,因此可以与C更好地相兼容。也就是说,你可以用Swift编写任何的库来供任何语言使用,只要这些语言可以使用C的库。这些特质保证了Swift拥有着比Java、C#、Python、Ruby以及Go更广阔的适用范围。后面这几个家伙,因为有GC的存在,不适合为其他语言提供基本库。我也很喜欢Go,但是毫无疑问,Swift会是个更加严谨与安全的语言。类型检测系统会帮助处理很多的错误,Go目前是很合适于Web开发但是不适合科学计算与游戏引擎这些你必须要大量自定义操作符来处理向量啊、矩阵运算这样的。

    因此,Swift可以被定义为一个安全并且用户友好的语言,并且可以像脚本语言那样方便实验与使用,其实swift想做移动端的python

     

    1.1  TypeAlias 类型别名:类似 C++ 的 typedef 和 Golang 的 type

    Swift 版本:

    typealias status = Int8

    C++ 版本:

    typedef status int

    Golang 版本:

    type status int

    1.2   Optional 可选类型: 类似 Haskell 中的 Maybe 类型

    Optional 可选类型,表示变量有值时返回存储的值,没有值返回 nil 。它可以在变量未声明的情况下确保后面调用的安全性。

    用法为 Optional<Type> 或者 Type? 。 例如:

    var i:Optional<Int>
    var str:String?

    Haskell 中也有类似的东西: Maybe 类型,它是这样定义的:

    data Maybe a =  Nothing | Just a deriving (Eq, Ord, Read, Show)

    用法也类似:

    i :: Maybe Int

    1.3  枚举支持元组:类似 Rust

    Swift 不仅支持基本的枚举类型,还可以是元组:

    enum Product
    {
        case Car(String, Int)
        case Phone(String, String)
    }

    Rust 中的枚举也有类似用法:

    enum Shape {
    	Circle { center: Point, radius: f64 },
    	Retangle { top_left: Point, bottom_right: Point }
    }

    1.4  用于 switch case 的 fallthrough: 类似 Golang 中的 fallthrough

    当初学 C 语言的时候就觉得 switch case 设计成默认贯穿很坑爹:实际开发中多数是不需要贯穿的,这就导致代码中一大堆 break 

    现在 Swift 终于从 Golang 吸收过来这个特性,一般不需要处理 case ,少数需要贯穿的情况才加上 fallthrough ,二者用法也类似,代码就不贴了。

    1.5  switch case 支持 where 语句:类似 Haskell 中 Pattern Guard 的 where 语句

    Swift 中的 switch case 语句支持 where 条件判断:

    let point = (-1, 2, 0)
    switch point
    {
    case (let x, _, _):
        println("x: \(x)")
        fallthrough
    case (x, y, z) where x * y * z == 0:
        println("x: \(x), y: \(y), z: \(z)")
    default:
        println()
    }

    Haskell 中虽然没有 switch case ,但有类似的 Pattern Guard,而且也支持 where 语句:

    funcBMI :: (RealFloat a) => a -> a -> String
    funcBMI weight height
    	| bmi <= bmi_thin = "Thin."
    	| bmi >= bmi_fat = "Fat."
    	| otherwise = "Normal."
    	where bmi = weight/height^2
    	      (bmi_thin,bmi_fat) = (18.5,30.0)

    1.6  支持函数类型:类似 C++11 中的 std::function 类型

    Swift 中可定义函数类型,并作为变量类型、参数类型、返回值类型:

    //定义函数类型:
    typealias FuncType = (Double, Double) -> Double
    
    //函数类型的变量:
    var funcDDD : FuncType
    
    //函数类型的参数:
    func printFuncResult(fun : FuncType, x : Double, y : Double) {
        println(fun(x, y))
    }
    
    //函数类型的返回值:
    func getFunc() -> FuncType {
        return funcDDD
    }

    甚至还可以将函数类型结合闭包使用:

    typealias FuncType = (Double, Double) -> Double
    
    func printFuncResult(x : Double, y : Double, fun : FuncType) {
        println("\(fun(x, y))")
    }
    
    //定义闭包:
    let funPlus = { (x : Double, y : Double) -> Double in return x + y }
    
    //将闭包作为函数类型的参数传入函数:
    printFuncResult(0.1234, 5.6789, funPlus)

    C++11 中的 std::function 也可定义一个函数指针,也有上述类似用法:

    //定义函数类型指针变量:
    std::function<double(double, double)> multiply;
    
    //将变量声明为该类型的 Lambda:
    multiply = [] (double x, double y) -> double {
    	return x * y;
    };
    
    //调用 Lambda:
    std::cout << multiply(1.234, 5.6789) << std::endl;

    1.7  支持运算符重载和定义新运算符:类似 C++ 中的运算符重载

    Swift 中可用 prefix 重载前缀运算符:

    prefix func - (p : Point) -> Point {
        return Point(x : -p.x, y : -p.y)
    }

    也可用 postfix 重载后缀运算符:

    postfix func ++ (p : Point) -> Point {
        var px = p.x, py = p.y
        return Point(x : ++px, y : ++py)
    }

    甚至可以使用 operator 定义新的运算符:

    prefix operator %& {}
    prefix func %& (p : Point) -> Point {
        return Point(x : p.x % 8, y : p.y % 8)
    }

    C++ 中的运算符重载那是出了名的强大,甚至可以重载 IO 运算符,这里仅给出基本用法:

    class Point {
    public:
    	Point(int i1, int i2, int i3): x(i1), y(i2), z(i3){}
    	Point operator-();
    	Point operator++();
    	bool operator==(const Point &p);
    	Point operator+(const Point &p);
    	int operator[](size_t n);
    private:
    	int x, y, z;
    	int values[3] = {x, y, z};
    };
    
    Point Point::operator-() {
    	return Point(-x, -y, -z);
    }
    
    Point Point::operator++() {
    	return Point(++x, ++y, ++z);
    }
    
    bool Point::operator==(const Point &p) {
    	return x == p.x && y == p.y && z == p.z;
    }
    
    Point Point::operator+(const Point &p) {
    	return Point(x + p.x, y + p.y, z + p.z);
    }
    
    int Point::operator[](size_t n) {
    	return values[n];
    }
    
    int main (int argc, char **argv) {
    	Point p1(1, 3, 5);
    	Point p2(2, 4, 6);
    	std::cout << (p1 == p2 ? "p1 == p2" : "p1 != p2") << std::endl;
    	Point p3 = -(p1 + p2);
    	//p3.operator++();
    	++p3;
    	std::cout << "p3: " << p3[0] << ", " << p3[1] << ", " << p3[2] << std::endl;
    	return 0;
    }

    1.8  函数参数可设置是否可变:类似 C++

    C++ 中在函数前添加 const 即可声明为不可变,代码就不贴了。

    Swift 中函数参数默认就是不可变的,如果要在函数内部修改参数的值,需要在参数前声明 var 关键字:

    func printContactVar(var name: String, email : String) {
        name = name.uppercaseString
        printContact(name: name, email: email)
    }
    

    上面的 var 参数虽然能修改值,但作用域仅限于函数内部,如果想在函数调用结束后,在外部依然生效,还可将关键字改为 inout 

    1.9  函数参数可设置默认值:类似 C++

    Swift 中定义函数时,可设置参数默认值:

    func printContact(name : String = "Rinc", email : String = "i@RincLiu.com") {
        println("\(name): \(email)")
    }

    C++ 也有类似特性:

    void printContact(std::string name = "Rinc", std::string email = "i@RincLiu.com");
    void printContact(std::string name, std::string email) {
    	std::cout << name << ": " << email << std::endl;
    }

    1.91  可通过 count 和 repeatedValue 初始化数组:类似 C++

    Swift 版本:

    var a = [Character](count: 10, repeatedValue: "X")

    C++ 版本:

    std::vector<char> v(10, 'x');

    1.92  元组相关特性:类似 Golang

    Swift 中可以通过 不取元组中的某个元素:

    let people = ("Rinc", age : 25)
    var (name, _) = people

    还有用于集合数据的遍历:

    var dic : Dictionary<String, Int> = ["Rinc" : 25, "Emma": 24]
    for (k, _) in dic {
        println("\(k)")
    }

    Golang 由于经常取回的数据集含有索引(连数组都有),所以这种用法更常见:

    mp := map[string]float32{"C": 5, "GO": 4.5, "JAVA": 6}
    for key, _ := range mp {
    	fmt.Println(key)
    }

    虽然 C++11、Rust 等语言也支持元组,但感觉大多简单用于函数多值返回,像上面这种用法貌似没见过;

    1.93  其他特性

    1. 函数语句结束不加分号:Golang、Rust 等很多语言都支持;

    2.  var 动态类型声明:JavaScript、Golang 等语言都支持;

    3. 函数多值返回:因为有了元组的支持,所以实现这个并不难,C++、Golang、Rust 等语言都支持;

    4. 闭包:这东西现在随着函数式编程的热门,几乎所有语言都提供了支持,也不多说了。

    总结

    总体来看 Swift 吸收的 C++ 特性最多,其次是 Golang 和 Hashekll,还有少量 Rust 特性。

     

    2. Performance

    Swift 具有一门高效语言所需要具备的绝大部分特点。与 Ruby 或者 Python 这样的解释型语言不需要再做什么对比了,相较于其前辈的 Objective-C,Swift 在编译期间就完成了方法的绑定,因此方法调用上不再是类似于 Smalltalk 的消息发送,而是直接获取方法地址并进行调用。虽然 Objective-C 对运行时查找方法的过程进行了缓存和大量的优化,但是不可否认 Swift 的调用方式会更加迅速和高效。

    另外,与 Objective-C 不同,Swift 是一门强类型的语言,这意味 Swift 的运行时和代码编译期间的类型是一致的,这样编译器可以得到足够的信息来在生成中间码和机器码时进行优化。虽然都使用 LLVM 工具链进行编译,但是 Swift 的编译过程相比于 Objective-C 要多一个环节 -- 生成 Swift 中间代码 (Swift Intermediate Language,SIL)。SIL 中包含有很多根据类型假定的转换,这为之后进一步在更低层级优化提供了良好的基础,分析 SIL 也是我们探索 Swift 性能的有效方法。

    最后,Swift 具有良好的内存使用的策略和结构。Swift 标准库中绝大部分类型都是 struct,对值类型的 使用范围之广,在近期的编程语言中可谓首屈一指。原本值类型不可变性的特点,往往导致对于值的使用和修改意味着创建新的对象,但是 Swift 巧妙地规避了不必要的值类型复制,而仅只在必要时进行内存分配。这使得 Swift 在享受不可变性带来的便利以及避免不必要的共享状态的同时,还能够保持性能上的优秀。

    编译器优化

    Swift 编译器十分智能,它能在编译期间帮助我们移除不需要的代码,或者将某些方法进行内联 (inline) 处理。编译器优化的强度可以在编译时通过参数进行控制,Xcode 工程默认情况下有 Debug 和 Release 两种编译配置,在 Debug 模式下,LLVM Code Generation 和 Swift Code Generation 都不开启优化,这能保证编译速度。而在 Release 模式下,LLVM 默认使用 "Fastest, Smallest [-Os]",Swift Compiler 默认使用 "Fast [-O]",作为优化级别。我们另外还有几个额外的优化级别可以选择,优化级别越高,编译器对于源码的改动幅度和开启的优化力度也就越大,同时编译期间消 耗的时间也就越多。虽然绝大部分情况下没有问题,但是仍然需要当心的是,一些优化等级采用的是激进的优化策略,而禁用了一些检查。这可能在源码很复杂的情 况下导致潜在的错误。如果你使用了很高的优化级别,请再三测试 Release 和 Debug 条件下程序运行的逻辑,以防止编译器优化所带来的问题。

    值得一提的是,Swift 编译器有一个很有用的优化等级:"Fast, Whole Module Optimization",也即 -O -whole-module-optimization。在这个优化等级下,Swift 编译器将会同时考虑整个 module 中所有源码的情况,并将那些没有被继承和重载的类型和方法标记为 final,这将尽可能地避免动态派发的调用,或者甚至将方法进行内联处理以加速运行。开启这个额外的优化将会大幅增加编译时间,所以应该只在应用要发布的时候打开这个选项。

    虽然现在编译器在进行优化的时候已经足够智能了,但是在面对编写得非常复杂的情况时,很多本应实施的优化可能失效。因此保持代码的整洁、干净和简单,可以让编译器优化良好工作,以得到高效的机器码。

    3. 注释与换行

    注释

    请将你的代码中的非执行文本注释成提示或者笔记以方便你将来阅读。Swift 的编译器将会在编译代码时自动忽略掉注释部分。

    Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠作(//)为起始标记:

    // 这是一个注释

    你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(/),终止标记为一个星号后跟随单个正斜杠(/):

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

    与C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:

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

    通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。

    4. Objective-C混合编程

    参考资料

    • 从Objective-C到Swift

    • swift与objective-c混编

    • Swift and Objective-C in the Same Project

    Swift 与 Objective-C混合调用示意图

    5. Swift类引用Objective-C文件

    因为Swift没有内嵌的头文件机制,因此Swift调用Objective-C需要一个名为“<工程名>-Bridging-Header.h”的桥接头文件。桥接头文件的作用是为Swift调用Objective-C对象搭建一个桥,它的命名必须是“<工程名>- Bridging-Header.h”,我们需要在桥接头文件中引入Objective-C头文件,所有的Swift类会自动引用这个头文件。

    桥接文件

    桥接文件设置

    • OJC类如下:

    //
    //  ObjcFunc.h
    //
    
    # import <Foundation/Foundation.h>
    
    @interface ObjcFunc : NSObject
    
    -(NSString*)sayHello:(NSString*)greeting withName: (NSString*)name;
    
    @end
    
    //
    //  ObjcFunc.m
    //
    
    # import "ObjcFunc.h"
    
    # import "CombinedProgram-Swift.h"
    
    @implementation ObjcFunc
    
    - (NSString*)sayHello:(NSString*)greeting withName: (NSString*)name{
    
        NSString *string = [NSString stringWithFormat:@"Hi,%@ %@.",name,greeting];
    
        return string;
    
      }
    
    @end
    • Swift类中调用

    import Foundation
    @objc class SwiftFunc: NSObject {
      func sayHello() -> Void {
      var obj : ObjcFunc = ObjcFunc()
      println(obj.sayHello("Hello", withName: "Swift"));
      return
      }
    }

    6. Objective-C类引用Swift文件

    (1)在Building Settings -> Packaging -> Defining中选定Module Name;

    (2)在OJC的头文件中引入:#import "{ModuleName}-swift.h"

    SwiftFunc* obj = [[SwiftFunc alloc] init];
    [obj sayHello];

    有时候会发现Xcode无法自动生成*-Swift.h文件,可以参考StackOverflow上的这篇文章。该文章总结下来,我们需要进行以下两大步检测:

    (1)检测你的Xcode的配置

    Product Module Name : myproject
    
    Defines Module : YES
    
    Embedded Content Contains Swift : YES
    
    Install Objective-C Compatibility Header : YES
    
    sObjective-C Bridging Header : $(SRCROOT)/Sources/SwiftBridging.h

    (2)检查你的Swift类是否正规

    要保证你的Swfit类中已经使用@objc关键字声明了一个继承自NSObject的类。Xcode不会为存在任何编译错误的类进行编译操作。

    (3)忽略Xcode的报错,先编译一下

    7. 语法要点

    ! VS ?

    在Swift中经常看到!与?两个操作符,譬如在类型转换、可选类型构造中都用到,用Apple官方的话说:

    It may be easiest to remember the pattern for these operators in Swift as: ! implies “this might trap,” while ?indicates “this might be nil.”

    就是!操作符表示我不管你编译器,我肯定要这么做,那么有可能导致运行时崩溃。而?操作符表示这个可能是nil,你帮我查查有没有进行完备的空检查。

    二. 以上是从语言技术角度,而如果从工程角度

    1, 生态环境比语言成熟慢:Swift非常适合思考,但是目前还不是工程化ready的语言,主要是上下游生态环境还不成熟,需要时间;

    2,路径依赖效应:人的路径依赖无解;未来使用swift并获得巨大市场成功的公司,不是今天这些市场上的巨无霸;就像诺基亚比苹果更早理解触屏的价值,但仍然只能眼睁睁看着苹果后来居上;普朗克曾经说过一句关于科学真理的真理,叙述为“一个新的科学真理取得胜利并不是通过让它的反对者们信服并看到真理的光明,而是通过这些反对者们最终死去,熟悉它的新一代成长起来。”对于技术来说也是如此。
     

    Reference

    Tutorial & Docs

    • 中文版 Apple 官方 Swift 教程《The Swift Programming Language》

    • Swift学习笔记

    Forum & Lessons

    Blog & News

    Book & Resources

    展开全文
  • 文档资源学习笔记正逐步更新于简书:https://www.jianshu.com/nb/39027334
  • swift4.1中文版

    2020-07-28 23:32:36
    swift4.1中文版
  • Swift 3 中的新特性

    万次阅读 2016-07-04 14:28:18
    原文:What’s New in Swift 3? 作者: Ben Morrow 译者:kmyhy Swift 3 将在下半年退出,对于 Swift 程序员来说,它带来了许多改变。 如果你没有密切关注 Swift Evolution 项目,那么在将代码迁移到 Swift 3 ...

    原文:What’s New in Swift 3?
    作者: Ben Morrow
    译者:kmyhy

    Swift 3 将在下半年推出,对于 Swift 程序员来说,它带来了许多改变。
    如果你没有密切关注 Swift Evolution 项目,那么在将代码迁移到 Swift 3 时,你可能不知道它到底有什么改变,以及它会对你的代码带来什么影响。那么你可能需要读一下这篇文章了。
    通过本文,我将重点介绍 Swift 3 中的重大改变,这些改变对你的代码有深刻影响。让我们开始吧!

    开始

    目前,Swift 3 仅在 Xcode 8 beta 版中有效。在未来几个月中,Swift 3 仍然在不断改变中,它还会发生一些变化。在 2016 年末,Xcode GM 发布时,Swift 3 的新特性才会固定下来。因此你应该沉住气,直到那个时候才可以向 App Store 提交用 Swift 3 编写的 app。
    为了便于开发者们将代码迁移到 Swift 3,苹果在 Xcode 8 中增加了一个 Swift 版本,即 Swift 2.3,对 Swift 作了一个小小的升级。如果你是一个开发者,Swift 2.3 和 2.2 实际并无不同,但 2.3 能够支持在 WWDC 中提到的新的 SDKs 和 Xcode 特性。在 Xcode 8 推出 beta 版时,你可以用 Swift 2.3 来提交你的 app,而不用将代码迁移到 Swift 3。
    我建议你在 Playground 中测试本文讨论的新特性,还可以用你某个项目来测试 Migration Assistant,以便你对这些改变有所了解。由于在 Xcode 8 beta 下一个版本及 Siwft 3 正式发布之前,你无法向 App Store 提交 app,你可以暂时不要讲代码迁移到 Swift 3,一直到这些问题得到解决。

    升级到 Swift 3

    在升级到 Swift 3 时,你会发现,基本上每个文件都需要改动!之所以这样,是因为所有的 Cocoa API 名称都被改变了。简而言之,API 仍然是原来的 API,但这个 API 在 Objective-C 中是一种叫法,而在 Swift 中又是另一种叫法了。Swift 3 语法书写起来要更贴近于自然语言。
    在 Xcode 8 中苹果提供了 Migration Assistant,它可以完成大部分的迁移工作。但很显然,仍然有一部分工作需要你手动完成。
    你可以立即将代码升级到 2.3 或者 3.0。如果你需要将代码又转回来,你可以用 Xcode 的 Edit > Convert > To Current Swift Syntax… 菜单。编译器会和 Migrateion Assistant 一样智能。如果你在调用方法时,偶然使用了老的 API,编译器会显示一个 Fixt-It 选项,让你使用正确的新 API。幸好 Swift 3 在最终发布时,才会停止改变源代码。因此,你可以将你的 Swift 代码保存为不同的版本。但是 Swift 核心团队不能保证这一点以后不会改变,如果在某个时候不在保证源码上的兼容,他们会提供一个较长的过渡期。这意味着源码是稳定的,这样能鼓励更多的保守的公司去使用它。
    这也说明,二进制稳定的目标还没有达到。本文最后将讨论这将导致的影响。

    已实现的 Swift Evolution 提议

    自从 Swift 开源以来,社区成员已经提交了超过 100 个改进建议。其中大部分(70多个)提议经过讨论修改之后已经被接受。还有一些仍然在激烈的争论之后被拒绝了。但最终,所有提议都要经过核心团队的最终拍板。
    核心团队和广大社区之间的合作是卓有成效的。事实上,Swift 在 Github 上获得了 30,000 多颗星。每周都会有新的提议提交,周周如此。甚至苹果的工程师也会在 Github 上提交他们的提议。

    在下一节中,你会看到一些类似 [SE-0001] 这样的标注。这是已经接受的提议编号,并且将在最终版的 Swift 3.0 中进行实现。每项提议都会标出,以便你查看每项改变的具体细节。

    API 的改变

    Swift 3 中最大的改变是标准库中在每个库中都采用了统一命名方式。API Design Guidleines中包含了这些规则,核心团队在构建 Swift 3 时采用了这些规则,对新手来说,这高度增强了可读性和易用性。核心团队遵循的是”好的 API 设计应当总是从调用者的角度看待问题“的原则。他们努力让 API 简单易用。不再多说,让我们开始介绍这些对你来说非常重要的改变。

    第一个参数的 label

    让我们从你每天都会在 Swift 中用到的例子开始。

    在函数或方法中的第一个参数现在必须有一个 label ,除非你显式地声明不要。以前,我们调用一个函数或方法时,可以忽略第一个参数的 label[SE-0046]:

    // 第一句是 Swift 2 语法,第二句是 Swift 3 语法
    
    "RW".writeToFile("filename", atomically: true, encoding: NSUTF8StringEncoding)
    "RW".write(toFile: "filename", atomically: true, encoding: NSUTF8StringEncoding)
    
    SKAction.rotateByAngle(CGFloat(M_PI_2), duration: 10)
    SKAction.rotate(byAngle: CGFloat(M_PI_2), duration: 10)
    
    UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
    UIFont.preferredFont(forTextStyle: UIFontTextStyleSubheadline)
    
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int
    override func numberOfSections(in tableView: UITableView) -> Int
    
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView?
    func viewForZooming(in scrollView: UIScrollView) -> UIView?
    
    NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
    NSTimer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(reset), userInfo: nil, repeats: true)
    

    注意,有些方法使用介词“of”、“to”、“with”、“in”作为外部参数名。这是为了增加代码的可读性。

    如果这个方法不使用介词也不使用 label,你应该在方法定义时,显式地在第一个参数名之前加一个下划线:

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { ... }
    override func didMoveToView(_ view: SKView) { ... }

    在许多编程语言中,许多方法可以共用一个方法名,但参数名不同。Swift 也不例外,现在,你可以重载方法,APIs 能够将直接将它们转换成合适的调用。下面是一个例子,展示了 index() 方法的两种重载形式:

    let names = ["Anna", "Barbara"]
    if let annaIndex = names.index(of: "Anna") {
      print("Barbara's position: \(names.index(after: annaIndex))")
    }

    方法名是同一个,但参数名不同,这将让人更容易记忆。

    省略不必要的单词

    过去,在苹果标准库中,方法名中会包含一个单词,用于表明方法的返回值。因为 Swift 编译支持类型推断,这种做法其实并不必要。核心团队尽可能过滤一切“噪音”,因此将这些重复的单词都删除了,只留下方法名中最重要的部分。

    在将 Objective-C 库转换成本地 Swift 语言方面,API 变得更智能了[SE-0005]:

    // 第一句是 Swift 2 语法,第二句是 Swift 3 语法
    
    let blue = UIColor.blueColor()
    let blue = UIColor.blue()
    
    let min = numbers.minElement()
    let min = numbers.min()
    
    attributedString.appendAttributedString(anotherString)
    attributedString.append(anotherString)
    
    names.insert("Jane", atIndex: 0)
    names.insert("Jane", at: 0)
    
    UIDevice.currentDevice()
    UIDevice.current()

    新的 GCD 和 Core Graphics

    一提到遗留下来的“元老级” API,GCG 和 Core Graphics 更需要被重新“装扮一新”。

    Grand Central Dispatch 常用于长时间计算或者与服务器通讯。将任务放到不同的线程,你可以避免阻塞用户界面。libdispatch 库是用 C 语言编写的,提供了 C 风格的 API。这个 API 现在在 Swift 中被重新设计为[SE-0088]:

    // Swift 2 语法
    let queue = dispatch_queue_create("com.test.myqueue", nil)
    dispatch_async(queue) {
        print("Hello World")
    }
    
    // Swift 3 语法
    let queue = DispatchQueue(label: "com.test.myqueue")
    queue.async {
      print("Hello World")
    }

    类似的情况还有 Core Graphics。Core Graphics 是用 C 编写的,曾经以来一直只能以“丑陋”的函数方式调用。这是它的新的用法[SE-0044]:

    // Swift 2 语法
    let ctx = UIGraphicsGetCurrentContext()
    let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
    CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor)
    CGContextSetStrokeColorWithColor(ctx, UIColor.whiteColor().CGColor)
    CGContextSetLineWidth(ctx, 10)
    CGContextAddRect(ctx, rectangle)
    CGContextDrawPath(ctx, .FillStroke)
    UIGraphicsEndImageContext()
    
    // Swift 3 语法
    if let ctx = UIGraphicsGetCurrentContext() {
        let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
        ctx.setFillColor(UIColor.blue().cgColor)
        ctx.setStrokeColor(UIColor.white().cgColor)
        ctx.setLineWidth(10)
        ctx.addRect(rectangle)
        ctx.drawPath(using: .fillStroke)
    
        UIGraphicsEndImageContext()
    }

    枚举中 case 值的大小写

    另一个和过去的 Swift 代码不同的地方是,在枚举中定义的 case 值现在使用小驼峰命名法。这是为了和属性名或者变量名保持一致[SE-0006]:

    // 第一句是 Swift 2 语法,第二句是 Swift 3 语法
    UIInterfaceOrientationMask.Landscape
    UIInterfaceOrientationMask.landscape
    
    NSTextAlignment.Right
    NSTextAlignment.right
    
    SKBlendMode.Multiply
    SKBlendMode.multiply

    大驼峰命名法现在只在类型名和协议名上使用。当你习惯这一切之后,Swift 团队对于追求一致性的努力才没有白费。

    返回值的方法或者修改值的方法

    标准库中对方法名中使用动词和名词的规定也更加统一。你应当根据这个方法会导致什么后果或者要采取一些动作来进行方法命名。首要原则是如果这个方法名中包含“ed”或“ing”后缀,则表明这是一个名词。方法名为名词的方法有返回值。如果不包含这些后缀,则很可能这是一个动词。以动词命名的方法会对某块引用的内存进行一些操作。即所谓的“修改某个值”。下面是几个符合名词/动词命名规则的方法[SE-0006]:

    customArray.enumerate()
    customArray.enumerated()
    
    customArray.reverse()
    customArray.reversed()
    
    customArray.sort() // changed from .sortInPlace()
    customArray.sorted()

    下面是一些使用这些方法的代码片段:

    var ages = [21, 10, 2] // 变量,不是常量,这样你才能修改它
    ages.sort() // 修改值,现在值变成了 [2, 10, 21]
    
    for (index, age) in ages.enumerated() { // "-ed" 是名词,表示会返回一个 ages 拷贝
      print("\(index). \(age)") // 打印:1. 2 \n 2. 10 \n 3. 21
    }
    
    ###函数类型
    
    函数在声明和调用时,都需要用括号将参数括住:
    
    ```swift
    func f(a: Int) { ... }
    
    f(5)
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    但是,当你用函数类型作为参数时,你可能会写出这样的代码:

    func g(a: Int -> Int) -> Int -> Int  { ... } // Swift 2 语法
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    你会发现代码很难读懂。参数在哪里结束,返回值从哪里开始?在 Swift 3 中,正确的定义方法是[SE-0066]:

    func g(a: (Int) -> Int) -> (Int) -> Int  { ... } // new way, Swift 3
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    现在,参数列表被括号包裹,然后才是返回类型。事情变得简单,同时函数类型更容易被识别出来。通过下面的比照,你会更清楚:

    // Swift 2 语法
    Int -> Float
    String -> Int
    T -> U
    Int -> Float -> String
    
    // Swift 3 语法
    (Int) -> Float
    (String) -> Int
    (T) -> U
    (Int) -> (Float) -> String
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    API 的增强

    除了对已有 API 进行“旧瓶新装”这个最大的改变以外——有非常多的 Swift 社区正致力于此,也有对 Swift API 的一些功能上的增加。

    访问所属类型

    当你定义一个静态属性或方法时,你直接通过类或类型来调用它们:

    CustomStruct.staticMethod()
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    如果你当前正在编写类型内部的代码,你还是要使用类型名来调用静态方法。为了使表述更加清晰,现在你可以通过 Self 来引用当前实例所属类型。S 为大写的 Self 表示引用当前实例的类型,而 s 为小写的 self 则引用当前实例自身。

    这是具体的例子[SE-0068]:

    struct CustomStruct {
      static func staticMethod() { ... }
    
      func instanceMethod() {
        Self.staticMethod() // 在类型内部
      }
    }
    
    let customStruct = CustomStruct()
    customStruct.Self.staticMethod() // 在一个实例上应用类型
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    行内 Sequences

    sequence(first:next:) 以及 sequence(state:next:) 是全局函数,返回一个无限序列。你传给它们一个初值或一个可变的状态,它们会在稍后调用闭包中的代码[SE-0094]:

    for view in sequence(first: someView, next: { $0.superview }) {
        // someView, someView.superview, someView.superview.superview, ...
    }
    
    
    
    
    <div class="se-preview-section-delimiter"></div>
    

    还可以用 prefix 操作符来为序列加一个限制[SE-0045]:

    for x in sequence(first: 0.1, next: { $0 * 2 }).prefix(while: { $0 < 4 }) { 
      // 0.1, 0.2, 0.4, 0.8, 1.6, 3.2
    }

    杂项

    • #keyPath() 等同于 #selector() ,帮助你减少输入错误
    • 你可以在某些类型上调用 pi,比如:Float.pi、CGFloat.pi。大部分时候编译器能够推断出类型:let circumference = 2 * .pi * radius [SE-0067]
    • NS 前缀从老的 Foundation 类型中移除,现在可以用 Calendar、Date来替代 NSCalendar、NSDate 了

    工具的改善

    Swift 只是一门语言,大部分时候你都无法离开书写它的开发环境——对于苹果开发者来说,也就是 Xcode!工具上的改变影响着每天编写代码的方式。
    Swift 3 修正了编译器和 IDE 中的 Bug,还改善了报告错误和信息的精确性。就像你所期望的,每个版本的发布都会让 Swift 和编译器的运行变得更快:

    • 改善了字符串的 Hash 算法,导致在将字符串存入字典后,性能有 3 倍的提升
    • 将对象从堆移到栈中存放,导致性能有 24 倍的提升(某些情况下)
    • 编译器可以一次缓存多个文件(在整个模块被优化过的情况下)
    • 优化了代码的大小,导致 Swift 代码的编译尺寸更小。以苹果的 Demobots 为例,编译尺寸缩减了原大小的 77%。

    Xcode 也会更加智能地理解 Swift 代码:

    • 过去,当你在一个 API 方法比如 sort() 上右击并跳转到定义时,你会看到一个不太容易理解的头文件。现在,在 Xcode 8 中,你会看到 sort() 方法实际上是 Array 类的扩展。

    • Swift Snapshots 就如今晚发布的 Swift Evolution 所说。在完全合并到 Xcode 之前,Snapshots 提供了一个使用新语法的机会。Xcode 8 能够在 playgournd 中加载和运行 Swift Snapshots。

    Swift 包管理器

    开源后的 Swift 实际上包含了 Swift 语言本身、核心库和包管理器。这三者一起构成了我们所见到的 Swift。包管理器定义了一个简单的目录结构,其中包括了你所共享和导入到项目中的 Swift 代码。
    类似于你所用过 Cocoapods 或 Carthage 这些包管理器,Swift 包管理器会下载依赖项并编译它们,把它们 link 成库或可执行文件。已经有 1000 个库支持 Swift 包管理器,并且在未来几个月中,还会有更多的库支持它。

    计划中的特性

    前面说过,Swift 3 尽量避免不兼容的改变,从而使你的代码能够从一个版本与下一个版本兼容。如果这是真的,那么在这次发布中应该还有一些更远大的目标未能达到,即泛型增强(generic additions)和 ABI(程序二进制接口)稳定。
    泛型增强包括递归协议约束和能够将一个受约束的扩展符合新协议(例如,一个用于存放 Equatable 对象的数组也应当遵守 Equatable 协议)。在这些特性尚未实现之前,Swift 不能被视作 ABI 稳定的。
    ABI 稳定要求应用程序和库无论使用哪个版本的编译器编译,都能被 link 和能够彼此进行交互。这对于第三方库来说是个重要的进步,因为这样就可以不需要提供源码就可以封装出框架了,而现在新版本的 Swift 不但要这些第三方库修改代码,还要重新进行编译。
    此外,如果 ABI 稳定就可以移除包含在二进制文件中的 Swift 标准库,因为就目前来说 iOS 和 macOS app 都是用 Xcode 创建的。目前的二进制文件中包含有 2 MB 左右的文件大小是为了确保能够兼容未来的操作系统。
    总之,你现在只能保持源代码的版本兼容,而二进制版本的兼容当前仍然是不可能的。

    结尾

    Swift 仍然在不断演进,因为社区的目标是最佳实践。虽然它还很年轻,但它同时也面临着无比巨大的机遇和美好未来。Swift 现在已经能够在 Linux 上运行了,在未来几年,你还会看见它在更多的服务器和设备上运行。从头设计一门语言必然会破坏 ABI 稳定性,一旦等它稳定后这种破坏就变得小多了。这(开源)是唯一能够让这门语言变得更好的机会,否则我们必将后悔莫及。
    Swift 正在扩大它的影响力。苹果正在身体力行。苹果的团队们正在 Music app、Console、Sierra画中画、Xcode 文档查看器、新的 iPad 版的Swift Playground app 中使用 Swift。
    说到这里,苹果好像有一种让非程序员学习 Swift 意图,例如可以在 iPad 上使用 Swift 以及某些教育措施都表明了这一点。
    另外,Swift 正在持续改进:命名变得更规范,代码读起来更清晰,你可以用工具来迁移代码。如果你想进一步深入了解,请看 WWDC 的会议视频
    可以肯定的是,2016年末 Swift 3 的正式发布必将令人期待。我们将持续跟进所有改变,请继续关注我们的教程、新书预告和视频,我们将开始使用这些令人兴奋的改进。

    Swift 3 中最令你感到兴奋的特性是哪个?你最希望我们介绍的又是哪个?请在下面的留言中告诉我们。

    展开全文
  • Swift关键字总结下篇

    千次阅读 2019-06-03 16:53:35
    Swift 中有多少关键字? 在Swift官方文档的词汇结构中, 有非常多的关键字, 它们被用于声明中、语句中、表达式中、类中、模式中, 还有以数字符号#开头的关键字, 以及特定上下文环境使用的关键字。本文中涉及的代码...

    Swift关键字总结上篇
    Swift关键字总结下篇


    Swift中有多少关键字?

    在Swift官方文档的词汇结构中, 有非常多的关键字, 它们被用于声明中、语句中、表达式中、类中、模式中, 还有以数字符号开头的关键字, 以及特定上下文环境使用的关键字。本文中涉及的代码可以在这里下载代码资源

    另外, 在特性中还有一些关键字, 是以@开头的关键字。这些所有的关键字将在 Swift关键字总结上篇Swift关键字总结下篇 两篇文章中详细列举。

    上篇中主要写到不带符号的关键字, 那么本篇中将详细写到下面的这些关键字。如带#或者@的关键字, 到底是如何使用的。

    起始于数字标记(#)的关键字

    #available#column#else#elseif#endif#file#function#if#line#selector#sourceLocation

    特性中的关键字(@)

    @available@discardableResult@GKInspectable@nonobjc@objc@NSCopying@NSManaged@objcMembers@testable@NSApplicationMain@UIApplicationMain@IBAction@IBOutlet@IBDesignable@IBInspectable@autoclosure@escaping@convention

    以数字符号#开头的关键字

    #available

    Swift 拥有内置的对 API 可用性的检查功能。编译器在 SDK 中使用可用性信息来确保在你项目中明确的 API 都是可用的。如果你尝试使用一个不可用的 API 的话,Swift 会在编译时报告一个错误。

    if #available(platform name version, ..., *) {
        statements to execute if the APIs are available
    } else {
        fallback statements to execute if the APIs are unavailable
    }
    

    在这个通用的格式中,可用性条件接收平台的名称和版本列表。你可以使用 iOS,macOS 和 watchOS 来作为平台的名字。要说明额外的特定主版本号则使用类似 iOS 8 这样的名字,你可以明确更小一点的版本号比如 iOS 8.3 和 macOS 10.10.3。

    举个例子:

    if #available(iOS 10, macOS 10.12, *) {
        // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
    } else {
        // Fall back to earlier iOS and macOS APIs
    }
    

    #file、#column、#line、#function

    这些都是特殊字面量。字面量表达式要么由普通字面量组成(例如字符串和数字), 要么是数组或字典的字面量、playground 字面量,要么就是这些特殊字面量。

    Literal Type Value
    #file String 它出现的位置的文件名
    #line Int 它出现位置的行数
    #column Int 他开始的列数
    #function String 它出现的声明

    直接上代码:

    class SomeClass {
        func logLiteral(fileName: String = #file, methodName: String = #function, lineNumber: Int = #line, column: Int = #column) {
            print("\(fileName as NSString)->\(methodName)->\(lineNumber)->\(column)");
        }
        
        func excuteLog() {
            logLiteral()
        }
    }
    SomeClass().excuteLog()
    

    在我工程中它是这个样子的:
    这里写图片描述

    所以打印结果为:

    MyPlayground.playground->excuteLog()->95->19
    

    #if、#end、#sourceLocation

    编译器控制语句允许程序改变编译器的行为。Swift 有两种编译器控制语句:编译配置语句线路控制语句

    编译配置语句可以根据一个或多个配置来有条件地编译代码。
    每一个编译配置语句都以 #if 开始,#endif结束。如下是一个简单的编译配置语句:

    #if 编译配置1
        如果编译配置1成立则执行这部分代码
    #elseif 编译配置2
        如果编译配置2成立则执行这部分代码
    #else
        如果编译配置均不成立则执行这部分代码
    #endif
    

    编译配置可以是 true 和 false 的字面量,也可以是使用 -D 命令行标志的标识符,或者是下列表格中的任意一个平台检测函数。

    函数 可用参数
    os() OSX, iOS, watchOS, tvOS, Linux
    arch() i386, x86_64, arm, arm64
    swift() >= 后跟版本号
    #if os(iOS)
        print("come in one")
    #endif
    #if arch(x86_64)
        print("come in two")
    #endif
    #if swift(>=4.0)
        print("come in three")
    #endif
    
    // result is
    // come in one
    // come in two
    // come in three
    

    介绍完编译配置语句, 然后开始介绍线路制语句。行控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。

    行控制语句形式如下:

    #sourceLocation(file: 文件名 , line:行号)
    #sourceLocation()
    

    第一种的行控制语句会改变该语句之后的代码中的字面量表达式 #line 和 #file 所表示的值。行号 是一个大于 0 的整形字面量,会改变 #line 表达式的值。文件名 是一个字符串字面量,会改变 #file 表达式的值。

    第二种的行控制语句, #sourceLocation(),会将源代码的定位信息重置回默认的行号和文件名。

    这里写图片描述

    @Attributes(特性)

    特性给有关声明或类型提供更多的信息。在 Swift 中有两种特性,一种用于声明,另一种用于类型。

    通过在 @ 符号后跟一个特性名称和该特性可以接受的实际参数来指定一个特性。有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些特性的参数写在圆括号内,它们的格式由它们所属的特性来定义:

    @特性名
    @特性名(特性参数)
    

    声明特性

    声明特性只能应用于声明。

    @available

    available 特性用于声明时,表示该声明的生命周期与特定的平台和操作系统版本有关。

    参数说明:

    1. available 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:iOS, OSApplicationExtension, macOS, macOSApplicationExtension, watchOS, watchOSApplicationExtension, tvOS, tvOSApplicationExtension, swift

    当然,你也可以用一个星号(*)来表示上面提到的所有平台。 其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。

    1. unavailable参数表示该声明在指定的平台上是无效的。

    2. introduced 参数表示指定平台从哪一版本开始引入该声明。格式如下:

    introduced=版本号
    

    版本号由一个或多个正整数构成,由句点分隔的。

    1. deprecated参数表示指定平台从哪一版本开始弃用该声明。格式如下:
    deprecated=版本号
    

    可选的版本号由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时无需给出任何有关信息。如果你省略了版本号,冒号(:)也可省略。

    1. obsoleted 参数表示指定平台从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台中移除,不能再被使用。格式如下:
    obsoleted=版本号
    

    版本号由一个或多个正整数构成,由句点分隔的。

    1. message 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下:
    message=信息内容
    

    信息内容由一个字符串构成。

    1. renamed 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下:
    renamed=新名字
    

    新名字由一个字符串构成。

    举例说明:
    你可以将renamed 参数和 unavailable 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。

    // 首发版本
    protocol MyProtocol {
    // 这里是协议定义
    }
    // 后续版本重命名了 MyProtocol
    protocol MyRenamedProtocol {
    // 这里是协议定义
    }
    @available(*, unavailable, renamed:"MyRenamedProtocol")
    typealias MyProtocol = MyRenamedProtocol
    

    还可以可以简明地表达出声明在多个平台上的可用性。如果 available 特性除了平台名称参数外,只指定了一个 introduced 参数,那么可以使用以下简写语法代替:

    @available(平台名称 版本号,*
    @available(macOS 10.12, *)
    @available(iOS 10.0, macOS 10.12, *)
    @available(swift 3.0.2)
    @available(*, deprecated: 10.0)
    @available(iOS, introduced: 2.0, deprecated: 8.0, message: "Header views are animated along with the rest of the view hierarchy")
    

    @discardableResult

    该特性用于的函数或方法声明, 以抑制编译器中函数或方法的返回值被调而没有使用其结果的警告。

    class WaringClass {
        @discardableResult
        func someWarningMethod() -> Bool {
            return true
        }
    }
    
    var waring = WaringClass()
    waring.someWarningMethod()
    

    这里写图片描述

    这里写图片描述

    @GKInspectable

    用这个特性可以把一个自定义的 GameplayKit 组件属性显示到 SpriteKit 编辑器界面中。使用这个特性也就隐式地使用了 objc 特性

    @objc

    该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。objc 特性告诉编译器这个声明可以在 Objective-C 代码中使用。需要注意的是添加objc修饰符并不意味着这个方法或者属性会变成动态派发, Swift依然可能会将其优化为静态调用。

    标有 objc 特性的类必须继承自 Objective-C 中定义的类。如果你将 objc 特性应用于一个类或协议,它也会隐式地应用于类或协议中兼容 Objective-C 的成员。对于标记了 objc 特性的类,编译器会隐式地为它的子类添加 objc 特性。标记了 objc 特性的协议不能继承没有标记 objc 的协议。

    objc特性同样会在下面的情况中隐式地添加:

    • 声明是子类的重写,并且父类的声明有 objc 特性;
    • 声明满足的需求来自一个拥有 objc 特性的协议;
    • 声明有 IBAction , IBOutlet , IBDesignable , IBInspectable , NSManaged , 或者 GKInspectable 特性。

    如果你在一个枚举中使用 objc 特性,枚举名和每个成员名串联起来,作为枚举成员暴露给 Objective-C 代码。成员名首字母大写。例如,在Swift中 Planet 枚举成员叫做 venus ,它作为一个叫 PlanetVenus 的成员暴露到 Objective-C 代码中。

    objc 特性可以接受一个特性实际参数,由一个标识符组成。当你想在 Objective-C 中为 objc 特性标记的实体暴露一个不同的名字时,用这个特性。你可以把这个实际参数用在命名类,枚举,枚举成员,协议,方法,gettersetter,初始化器。下面的例子把 ExampleClass 中 enabled 属性的 getter 作为 isEnabled 暴露给 Objective-C 代码,而不仅仅是属性本身的名字。

    //@objc
    class ExampleClass: NSObject {
        @objc var enabled: Bool {
            @objc(isEnabled) get {
                // Return the appropriate value
                return true
            }
        }
    }
    

    @nonobjc

    把这个特性应用到一个方法,属性,下标,或者初始化器的声明中,废除一个隐式 objc 特性 。尽管有可能在 Objective-C 中表示一个声明, nonobjc 特性告诉编译器,使其声明在 Objective-C 代码中不可用。

    给扩展使用这个特性与对扩展中的所有不显式地用 objc 特性标记的成员使用是一样的效果。

    对于一个标为 objc 特性的类中桥接的方法,你可以使用 nonobjc 特性解决其循环性,并且允许重载标为 objc 特性的类中的方法和初始化器。

    一个标记为 nonobjc 特性的方法不能重写标为 objc 特性的方法。但是,一个标记为 objc 特性的方法可以重写一个标为 nonobjc 特性的方法。同样,一个标为 nonobjc 特性的方法不能满足一个标为 objc 特性方法的协议需求。

    @NSCopying

    这个特性用于一个类的可变存储属性中。这个特性让属性值(由 copyWithZone(_:) 方法返回,而不是属性本身的值)的拷贝合成属性的setter。属性的类型必须遵循 NSCopying 协议。

    从某种程度上来说, NSCopying 特性的行为类似 Objective-C 中的 copy 属性特性。@NSCopying修饰的属性必须是遵循NSCopying协议的,而在 Swift 中, NSCopying协议被限制给类使用。由于 Swift 中的 String 类型已经是值类型了。所以 String 不能通过扩展遵循NSCopying协议。值类型的赋值,只可能是值的拷贝,当然,结构体里面有对象类型,就另当别论了。

    以下是一个使用@NSCopying的案例:

    class Dog : NSObject, NSCopying {
        var name = "no name"
        var age = 0
        
        func copy(with zone: NSZone? = nil) -> Any {
            let copy = Dog()
            print("copyed")
            copy.name = name
            copy.age = age
            return copy
        }
    }
    
    class Master : NSObject {
        @NSCopying var pet : Dog
        init(pet : Dog) {
            self.pet = pet
            super.init()
        }
    }
    
    // create dogA
    var dogA = Dog()
    dogA.name = "dididi"
    dogA.age = 1
    
    // create dogB
    var dogB = Dog()
    dogB.name = "dadada"
    dogB.age = 3
    
    // create master of dogA
    var master = Master(pet: dogA)
    
    print(master.pet === dogA)
    print(master.pet.name, master.pet.age)
    // true
    // dididi 1
    
    // dogB replace dogA
    master.pet = dogB
    // copyed
    
    print(master.pet === dogB)
    print(master.pet.name, master.pet.age)
    // false
    // dadada 3
    

    大部分情况下,@NSCopying在 Swift 中的使用场景都是放在调用 Objective-C 中的类型时候使用。自定义类去遵循NSCopying协议,不太符合 Swift 的类型体系:请使用结构体!

    @NSManaged

    该特性用于修饰 NSManagedObject 子类中的实例方法或存储型变量属性,表明它们的实现由 Core Data 在运行时基于相关实体描述动态提供。对于标记了 NSManaged 特性的属性,Core Data 也会在运行时为其提供存储。应用这个特性也意味着objc特性。

    在与Core Data 模型中管理对象子类相关的特性或者关系的每个属性定义之前,将@NSmanaged特性加入。与 Objective-C 里面的 @dynamic特性类似,@NSManaged特性告知 Swift 编译器,这个属性的存储和实现将在运行时完成。但是,与@dynamic不同的是,@NSManaged特性仅在 Core Data 支持中可用。

    Swift 类被命名空间化—他们局限于被编译的模块中(最典型的是Target)。 为了使用带 Core Data 模型的NSManagedObject类的 Swift 子类,在模型实体监视器的类区域里,用模块名字作为类名的前缀。

    @testable

    在导入允许测试的编译模块时,该特性用于修饰 import 声明,这样就能访问被导入模块中的任何标有 internal 访问级别修饰符的实体,犹如它们被标记了 public 访问级别修饰符。测试也可以访问使用internal或者public访问级别修饰符标记的类和类成员, 就像它们是open访问修饰符声明的。

    当你在写一个有单元测试目标的应用时,你的代码应该能被模块访问到以进行测试。默认情况下只有标注为 openpublic 的才可以被其他模块访问。但是,如果你使用 @testable 属性标注了导入的生产模块并且用使能测试的方式编译了这个模块,单元测试目标就能访问任何 internal 的实体。

    @testable import someModule
    

    @UIApplicationMain

    我们先来看一个问题,创建一个基于 Objective-C 的 iOS App 工程。XCode 目录下会自动创建一个main.m文件。里面的代码段如下:

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    其实上面这段代码在OC工程的main.m中所做的事情, 就是 Swift 的项目中引入 @UIApplicationMain特性的作用。苹果对 Swift 的项目进行了简化,在 iOS App 工程的某个类上使用@UIApplicationMain特性,就表示该类是当前应用程序的代理类。这是十分便利的,也是当你创建 Swift iOS工程时,系统会自动生成的 AppDelegate.swift 里面,会看到以下代码:

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    ...
    

    了解@UIApplicationMain特性后,再来看一看不使用这个特性需要怎么做呢?如果不通过@UIApplicationMain特性的话,我们也可以通过调用 UIApplicationMain(_:_:_:)函数并且把该类的名字作为代理类的类型传递进函数,来达到同样的效果。具体做法是,自己提供一个 main.swift 文件,并在代码顶层调用 UIApplicationMain(_:_:_:) 函数来设置自己定义的ApplicationAppDelegate

    举个例子,比如说我要自定义一个Application去监听所有的Event,那么我就可以通过UIApplicationMain(_:_:_:) 函数来传入自己定义的Application了。代码如下:

    import UIKit
    
    class MyApplication: UIApplication {
        override func sendEvent(_ event: UIEvent) {
            super.sendEvent(event)
            print("截获的Event出发时间戳: \(event.timestamp)");
        }
    }
    
    UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv,
                      NSStringFromClass(MyApplication.self),
                      NSStringFromClass(AppDelegate.self))
    

    大家平时可以忽略这个特性,因为 Xcode 已经自动帮你加上了。不用自己添加任何东西,只需要关注代理类中要去实现的相关的业务逻辑。但是对这个特性的理解,有助于你去理解整个 app 的生命周期。当然也包括下面要说的@NSApplicationMain特性。

    @NSApplicationMain

    在某个类上使用@NSApplicationMain特性表示该类是应用程序代理类,使用该特性与调用 NSApplicationMain(_:_:) 函数并且把该类的名字作为代理类的类型传递给函数的效果相同。

    其实@UIApplicationMain@NSApplicationMain 两个特性所做的事情是一样,唯一的区别是针对的项目不同。在OSX 桌面应用的开发中,使用@NSApplicationMain ;在 iOS App 工程中使用@UIApplicationMain。所以当你使用Xcode 创建一个 Swift MacOS工程时,自动生成的 AppDelegate.swift 里面,会看到以下代码:

    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
    ...
    

    当然,如果你不想使用这个特性,也可以提供一个 main.swift 文件,并在代码顶层调用NSApplicationMain(_:_:) 函数,如下所示:

    import AppKit
    NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
    

    Interface Builder使用的声明特性

    Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特性。Swift 提供了以下的 Interface Builder 特性:IBActionIBOutletIBDesignable,以及IBInspectable 。这些特性与 Objective-C 中对应的特性在概念上是相同的。

    IBOutletIBInspectable 用于修饰一个类的属性声明,IBAction 特性用于修饰一个类的方法声明,IBDesignable 用于修饰类的声明。
    IBActionIBOutlet 特性都意味着objc特性。

    类型特性

    类型特性只能用于修饰类型。

    @autoclosure

    这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。

    自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。自动闭包允许你延迟处理,因此闭包内部的代码直到你调用它的时候才会运行。对于有副作用或者占用资源的代码来说很有用,因为它可以允许你控制代码何时才进行求值。

    函数类型的形式参数() -> T (其中 T 是任何类型)可以应用 autoclosure 特性在其调用时隐式创建闭包。这提供了语法上方便的方式来推迟表达式的执行而不需要在调用函数时写一个显式的闭包。比如说一个() -> String的闭包, 用@autoclosure 来修饰。现在你可以调用函数就像它接收了一个 String 实际参数而不是闭包,而实际参数自动地转换为了闭包。

    下面来举个例子, 写一个函数, 入参中包含一个闭包, 而且这个闭包没有形式参数:

    // 1.完整闭包
    printIfTrue(block:  { () -> Bool in
        return 2 > 1
    })
    // 2.闭包中括号内的省略
    printIfTrue(block: { return 2 > 1 })
    // 3.尾随闭包的省略
    printIfTrue(){ return 2 > 1 }
    // 4.省略return
    printIfTrue(){ 2 > 1 }
    // 5.无入参时, 省略()
    printIfTrue{2 > 1}
    // 不使用自动闭包的用法, 如此调用会报错
    //printIfTrue(2 > 1)
    

    写一个同样功能的自动闭包的函数:

    func printIfTrueOrNot(block: @autoclosure ()-> Bool){
        if block(){
            print("The result is true")
        }
    }
    
    // 使用自动闭包, 相当于把 2 > 1 这个表达式的bool结果, 自动转换为 () -> Bool
    printIfTrueOrNot(block: 2 > 1)
    

    @escaping

    当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它可以在函数返回之后被调用。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。闭包可以逃逸的一种方法是被储存在定义于函数外的变量里。比如说,很多函数接收闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成——闭包需要逃逸,以便于稍后调用。

    将这个特性应用到一个方法或函数声明的形参类型中,以指明可以存储该形参值用于稍后执行。这意味着允许那个值超过调用它的范围而存在。 escaping 类型特性的函数类型形参需要为属性或方法显式使用 self.

    var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
        print("#1-刚刚进逃逸闭包函数, 准备开始添加---\(completionHandlers.count)")
        completionHandlers.append(completionHandler)
        print("#1-执行到我这里, 虽然已经将闭包添加进数组, 但是闭包还没有执行---\(completionHandlers.count)")
    }
    
    func someFunctionWithNonescapingClosure(closure: () -> Void) {
        print("#2-刚刚进入非非非逃逸闭包函数")
        closure()
        print("#2-代码执行结束了")
    }
    
    class SomeClassd {
        var x = 10
        func doSomething() {
            someFunctionWithEscapingClosure {
                print("#1-这里才是真正执行传入闭包的时刻---\(completionHandlers.count)")
                self.x = 100
            }
            someFunctionWithNonescapingClosure {
                print("#2-这里我进行了操作")
                self.x = 200
            }
        }
    }
    
    let instance = SomeClassd()
    instance.doSomething()
    print(instance.x)
    // Prints "200"
    
    completionHandlers.first?()
    print(instance.x)
    

    函数 someFunctionWithEscapingClosure(_:)接收一个闭包作为实际参数并且添加它到声明在函数外部的数组里。如果你不标记函数的形式参数为 @escaping,你就会遇到编译时错误。这里你可以很清楚的发现拥有逃逸闭包的函数和拥有非逃逸闭包的函数之间的区别。

    让闭包 @escaping意味着你必须在闭包中显式地引用 self,比如说,下面的代码中,传给 someFunctionWithEscapingClosure(_:)的闭包是一个逃逸闭包,也就是说它需要显式地引用 self。相反,传给someFunctionWithNonescapingClosure(_:) 的闭包是非逃逸闭包,也就是说它可以隐式地引用 self

    @convention

    convention 该特性用于修饰函数类型,它指出了函数调用的约定。该特性总是与下面的参数之一一起出现:
    1.swift 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
    2.block 参数用于表示匹配 Objective-C 方法参数中的block参数。函数值会作为一个block对象的引用,块是一种 id 兼容的 Objective-C 对象,其中嵌入了调用函数。遵守 C函数的调用约定。
    3.c 参数用于表示匹配 C 函数参数中的函数指针。函数值没有上下文,不具备捕获功能,同样遵守 C函数的调用约定。

    除了少数例外,当需要任何其他调用约定的函数时,可以使用任何调用约定的函数。非泛型全局函数,和局部函数或不捕获任何局部变量的闭包,可以转换为 C 调用约定。其他 Swift 函数和带有 Objective-C 闭包调用约束的函数不能转换为 C 调用约定。

    具体场景

    1.在 Swift 中调用包含函数指针参数的 C函数

    定义了某个C函数:

    CGFloat myCFunction(CGFloat (callback)(CGFloat x, CGFloat y)) {
        return callback(1.1, 2.2);
    }
    

    其中 callback是一个函数指针,需要调用者自己实现,在 Swift 中,如果需要实现callback,供myCFunction调用的话,有以下写法,这里就会用到@convention:

    let swiftCallback : @convention(c) (CGFloat, CGFloat) -> CGFloat = {
        (x, y) -> CGFloat in
        return x + y
    } 
    let result = myCFunction( swiftCallback )
    print(result) // 3.3
    

    另外,还有更加简单地直接使用闭包的做法,这里没有用到@convention:

    let result = myCFunction( {
        (x, y) -> CGFloat in
        return x + y
    } )
    print(result) // 3.3
    

    2.在 Swift中调用包含 block参数的 Objective-C方法

    与调用 C 的函数指针类似,要在 Swift 中调用一个含有 block 的 Objective-C 的方法时,需要使用@convention(block)定义 Swift 变量才能传入到 Objective-C 的方法中。当然也可以直接使用闭包,这里我们举一个动画方法的例子:

    [UIView animateWithDuration:2 animations:^{
    	NSLog(@"start");
    } completion:^(BOOL finished){
     	NSLog(@"completion");
    }];
    

    以上代码使用了 2个block,直接使用闭包转换成 Swift代码:

    UIView.animate(withDuration: 2, animations: {
        NSLog("start")
    }, completion: {
        (completion) in
        NSLog("completion")
    })
    

    等价使用@convention(block)的代码如下:

    let animationsBlock : @convention(block) () -> () = {
        NSLog("start")
    }
    let completionBlock : @convention(block) (Bool) -> () = {
        (completion) in
        NSLog("start")
    }
    UIView.animate(withDuration: 2, animations: animationsBlock, completion: completionBlock)
    

    相关资料:
    https://blog.csdn.net/wangyanchang21/article/details/78928925)
    copy关键字和NSCopying协议
    @NSApplicationMain 和 @UIApplicationMain
    @convention

    展开全文
  • 为何 iOS 越来越偏爱 Swift

    千次阅读 2018-10-25 00:37:45
    【CSDN编者按】本月初,苹果已经面向所有iPhone用户,推送了iOS 12.0.1正式版。那么,iOS 12.0中Swift的应用是个什么样子?iOS中使用Swift...
  • 在Windows下编写swift程序

    千次阅读 2020-03-03 18:45:47
    在Windows下编写swift程序 1.首先介绍一下本次使用到的软件Visual Studio Code和 Swift for Windows(见图1)。 Visual Studio Code(以下简称vscode)是一个轻量且强大的跨平台开源代码编辑器(IDE),支持Windows...
  • 国际结算三大方式之——汇款

    万次阅读 2016-11-11 08:55:23
    汇款(Remittance)业务是国际结算方式之一,帮助客户实现资金的转移。汇款分为汇出汇款和汇入汇款两种,银行间不涉及单据...当前汇款主要采取的方式通过电汇(TelegraphicTransfer,T/T),通过Swift系统实现,一般2
  • swift 中 把 String转成Int或Double。。。
  • 掌握ios基本组件使用,熟悉众多技巧
  • 使用swift有一段时间了,api的变换造成了很多困扰,下面是关于url编码和解码问题的解决方案 在Swift中URL编码encode在Swift中URL编码用到的是String的方法func addingPercentEncoding(withAllowedCharacters ...
  • iOS OC项目调用Swift

    万次阅读 2017-03-17 13:51:27
    网上有很多关于OC项目中使用Swift类的方法,但是亲自试了之后,发现不够详细,多次尝试后,终于找出详细的方法。 现把方法和遇到的问题,记录下来,方便其他同仁借鉴.
  • 爆炸性新闻
  • swift 学习资源 大集合

    万次阅读 2014-06-07 17:07:46
    今天看到了一个swift的学习网站,里面收集了很多学习资源 Swift 介绍 Swift 介绍  来自 Apple 官方 Swift 简介 (@peng_gong)  一篇不错的中文简介 [译] Swift 首席架构师 Chris Lattner ...
  • Swift 获得字符串String长度

    万次阅读 2020-02-19 16:17:39
    在Objective-C中我们通常使用length来获取NSString类型的字符串的长度,而在Swift中,String结构体没有了这个方法,那怎样获取String类型的字符串的长度呢?我们可以通过他的扩展属性成员characters的count属性来获取 ...
  • 掌握apple watch上的开发
1 2 3 4 5 ... 20
收藏数 143,951
精华内容 57,580
关键字:

swift