4.0关键字 swift_openstack swift 2.20.0 2.21.0 - CSDN
  • 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

    展开全文
  • Swift4.0】属性

    2017-12-28 15:19:36
    希望对Swift的学习者有所帮助,使用的编写工具:JQNote  InNote(iPhone) 属性是把值和某个类,结构体或者枚举联系在一起。存储型属性存储一个实例的常量和变量值部分,然而计算型属性会计算出(而不是...

    翻译能力有限,如有不对的地方,还请见谅!希望对Swift的学习者有所帮助,使用的编写工具:JQNote    InNote(iPhone)


    属性是把值和某个类,结构体或者枚举联系在一起。存储型属性存储一个实例的常量和变量值部分,然而计算型属性会计算出(而不是存储)一个值。类,结构体和枚举都支持计算型属性,而存储型属性只有类和结构体支持。这两种属性通常跟某个类型的实例关联。然而,属性也可以跟类型本身关联起来,这种属性称之为类型属性。另外,你可以定义一个属性观察者来检测该属性值的改变。


    存储属性

    一个存储属性是一个常量或者变量,被存储作为一个类或者结构体实例的一部分。你可以在定义它的时候给它一个缺省值,也可以在初始化方法中设置和修改存储属性的初始化值,即便是该存储属性是常量。如果你创建了一个常量结构体实例,那么就不能修改该实例的属性,即使属性被声明为变量。


    let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)

    // this range represents integer values 0, 1, 2, and 3

    rangeOfFourItems.firstValue = 6

    // this will report an error, even though firstValue is a variable property”


    因为结构体是值类型,当一个结构体实例被声明为常量,那么它的所有属性都不能被改变。但是对于类来说,是引用类型,如果类实例被声明为常量,你还可以改变它的变量属性的。


    懒存储属性

    这种属性直到它第一次被使用才会初始化。在声明存储属性前面加上lazy关键字就表示为懒存储属性。


    你必须总是声明懒存储属性为变量(var),因为直到实例初始化完成之后,它的初始化值可能不能被获取,而常量属性必须总是在实例初始化完成之前有一个值。


    class DataImporter {

        /*DataImporter is a class to import data from an external ”

    “file.

         The class is assumed to take a non-trivial amount of time to initialize.

         */


        var filename = "data.txt"

        // the DataImporter class would provide data importing functionality here

    }

     

    class DataManager {

        lazy var importer = DataImporter()

        var data = [String]()

        // the DataManager class would provide data management functionality here

    }

     

    let manager = DataManager()

    manager.data.append("Some data")

    manager.data.append("Some more data")”


    如上面的例子,因为importer 被声明为一个lazy 属性,DataImporter实例仅仅是当 importer属性第一次被调用获取的时候,才会创建。

    print(manager.importer.filename)

    // the DataImporter instance for the importer property has now been created

    // Prints "data.txt”


    计算型属性

    计算型属性实际上不会存储一个值,相反,它们提供了一个getter和一个可选的setter方法来直接获取或者设置其它属性的值。

    struct Point {

        var x = 0.0, y = 0.0

    }


    struct Size {

        var width = 0.0, height = 0.0

    }


    struct Rect {

        var origin = Point()

        var size = Size()

        var center: Point {

            get {

                let centerX = origin.x + (size.width / 2)

                let centerY = origin.y + (size.height / 2)

                return Point(x: centerX, y: centerY)

            }

            set(newCenter) {

                origin.x = newCenter.x - (size.width / 2)

                origin.y = newCenter.y - (size.height / 2)

            }

        }

    }


    var square = Rect(origin: Point(x: 0.0, y: 0.0),

                      size: Size(width: 10.0, height: 10.0))

    let initialSquareCenter = square.center

    square.center = Point(x: 15.0, y: 15.0)


    只读的计算型属性

    一个计算型的属性,如果只有getter方法,没有setter方法,那就被认为是一个只读的计算属性。你可以声明一个简单的只读计算属性,不需要get关键字:

    struct Cuboid {

        var width = 0.0, height = 0.0, depth = 0.0

        var volume: Double {

            return width * height * depth

        }

    }


    let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)

    print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")

    // Prints "the volume of fourByFiveByTwo is 40.0”


    属性观察者

    属性观察者观察和响应一个属性值的改变。当属性值每次被set的时候,观察者都会被调用,即使新的值和当前的值相同。除了lazy属性,你可以将属性观察者添加到任何存储属性:


    >> willSet :值被存储之前调用

    >> didSet :新值被存储之后调用


    当你实现了willSet,它会传递一个新的值作为一个常量参数。你也可以给这个参数特定一个名字。如果你没有写这个参数名,那么默认newValue可以被使用。类似,如果实现了didSet,你也可以特定一个参数名或者使用默认的oldValue。

    class StepCounter {

        var totalSteps: Int = 0 {

            willSet(newTotalSteps) {

                print("About to set totalSteps to \(newTotalSteps)")

            }

            didSet {

                if totalSteps > oldValue  {

                    print("Added \(totalSteps - oldValue) steps")

                }

            }

        }

    }


    类型属性

    实例属性属于某个类型的实例,每次在创建实例的时候,它有自己的一系列属性。与其它实例是分开的。

    你也可以定义属于类型自己的属性,而不属于每个该类型的实例。类型属性只会拷贝一次,无论该类型被创建了多少个实例。

    不像存储实例属性,存储类型属性必须赋一个缺省值,这是因为类型自己没有一个初始化器可以在初始化的时候给类型属性设置值。

    定义一个类型属性,使用staic关键字:

    struct SomeStructure {

        static var storedTypeProperty = "Some value."

        static var computedTypeProperty: Int {

            return 1

        }

    }

    enum SomeEnumeration {

        static var storedTypeProperty = "Some value."

        static var computedTypeProperty: Int {

            return 6

        }

    }

    class SomeClass {

        static var storedTypeProperty = "Some value."

        static var computedTypeProperty: Int {

            return 27

        }

        class var overrideableComputedTypeProperty: Int {

             return 107

        }

    }


    获取和设置类型属性:

    print(SomeStructure.storedTypeProperty)

    // Prints "Some value."

    SomeStructure.storedTypeProperty = "Another value."

    print(SomeStructure.storedTypeProperty)

    // Prints "Another value."

    print(SomeEnumeration.computedTypeProperty)

    // Prints "6"

    print(SomeClass.computedTypeProperty)

    // Prints "27”



    展开全文
  • The Swift Programming Language 4.0 中文版学习笔记 11. 基础部分注释嵌套注释: /* A /* B */ /常量和变量常量和变量名可以使用Unicode字符来命名,但不能包含数学符号,箭头,保留的(或者非法的)Unicode码位...

    The Swift Programming Language 4.0 中文版学习笔记 1

    1. 基础部分

    注释

    嵌套注释:
    /* A
    /* B */
    /

    常量和变量

    常量和变量名可以使用Unicode字符来命名,但不能包含数学符号,箭头,保留的(或者非法的)Unicode码位,连线与制表符。也不能以数字开头。

    常量和变量一旦声明为确定的类型,即不能改变其存储的值的类型。同时,常量和变量不能互转。

    数字

    除非需要特定长度的整数,一般来说使用Int就够了,这样可以提供代码一致性和可复用性。

    尽量不要使用UInt,除非需要存储一个和当前平台原生字长相同的无符号整数。否则,最好使用Int,即使要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断。

    Double类型的精确度高于Float,选择哪个类型取决于代码需要处理的值的范围,在两种类型都匹配的情况下,优先选择Double

    类型推断

    数字类字面量可由编译器推断类型,而数字类常量和变量必须显式转换为其他类型。

    类型别名

    类型别名typealias就是给现有类型定义另一个名字。

    元组

    可以把任何顺序的类型组合成一个元组,这个元组可以包含所有类型。

    可以将一个元组的内容分解为单独的常量和变量。如果只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记。

    定义元组的时候,可以给单个元素命名。

    let http200Status = (statusCode: 200, description: “OK")
    print(“status code: \(http200Status.statusCode), description: \(http200Status.1)”)
    let (justTheStatusCode, _) = http200Status

    可选类型

    可选类型可以暗示任意类型的值缺失,并不需要一个如Objective-C中的NSNotFound这样的特殊值来标记非对象类型。

    可以给可选变量赋值为nil来表示它没有值,nil不能用于非可选的常量和变量。

    Swift的nil和Objective-C的nil并不一样。后者是一个指向不存在对象的指针。在Swift中,nil不是指针——它是一个确定的值,用来表示值缺失。

    可选绑定

    使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含值就把值赋值给一个临时常量/变量。可选绑定可以用在ifwhile语句中。

    注意:在if条件语句中使用常量/变量来创建一个可选绑定,仅在if语句的句中(body)中才能获取到值。相反,在guard语句中使用常量/变量来创建一个可选绑定,仅在guard语句外且在语句后才能获取到值。

    隐式解析可选类型

    有时在程序架构中,第一次赋值之后可以确定一个可选类型总会有值。这种情况下,每次都要判断和解析可选类型是非常低效的。这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用做可选类型的后面的问号(String?)改为感叹号(String!)来声明一个隐式解析可选类型。

    注意:只在确定一个变量不可能变为nil时才使用隐式解析可选类型。否则在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误;正如和没有值的普通可选类型后面加一个惊叹号一样。

    2. 基本运算

    空合运算符(Nil Coalescing Operator)

    空合运算符(a ?? b)将对可选类型a进行空判断,如果包含一个值就进行解封,否则就返回一个默认值b。注意这里,a 必须是可选类型, 默认值b的类型必须与a存储值的类型保持一致。

    区间运算符(Range Operators)

    a…b // 闭区间运算符
    a..<b // 半开区间运算符
    array[2…] // 单侧区间1
    array[…2] // 单侧区间2

    3. 字符串和字符

    多行字符串字面量

    由一对三个双引号"""包裹着文本,可实现跨越多行的字符串。注意:从开启引号当前行之后的第一行开始,到关闭引号当前行之前的一行为止。可以在行尾写一个反斜杠(\)作为续行符来实现增强代码可读性且避免换行。

    关闭引号当前行之前的空白字符串告诉Swift编译器其他各行多少空白字符串需要忽略。

    字符串是值类型

    Swift的String是值类型。拷贝的方式保证了在函数/方法中传递的是字符串的值。

    连接字符串和字符

    字符串支持的运算符包括+ += == !=,也可以使用append()方法将一个字符附加到一个字符串变量的尾部。

    字符串插值

    字符串插值是构建新字符串的方式之一,可以包含常量、变量、字面量和表达式。

    4. 集合类型(Collection Types)

    Swift语言提供Array(有序数据集合)、Set(无序不重复数据集合)、 Dictionary(无序键值对集合)三种基本集合类型用来存储集合数据。它们存储的数据类型必须明确。

    集合的可变性

    如果集合赋值给一个变量,则这个集合是可变的;否则分配给一个常量,则是不可变的。

    数组(Array)

    数组Array支持使用加法运算符+和加法赋值运算符+=[]是空数组。

    注意:自定义类型通过实现Hashable协议,可以作为集合的值的类型或者是字典的键的类型。实现Hashable协议,需要提供一个类型为Int的可读属性hashValue

    集合(Set)

    你可以高效地完成Set的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。

    字典(Dictionary)

    通过访问keys或者values属性,我们也可以遍历字典的键或者值。

    5. 控制流

    Swift提供了多种流程控制结构,包括可以多次执行任务的while/(repeat-while)循环,基于特定条件选择执行不同代码分支的if、guard和switch语句,还有控制流程跳转到其他代码位置的break和continue语句。
    Swift 还提供了for-in循环,用来更简单地遍历数组(Array),字典(Dictionary),区间(Range),字符串(String)和其他序列类型。
    Swift 的switch语句比 C 语言中更加强大。case 还可以匹配很多不同的模式,包括范围匹配,元组(tuple)和特定类型匹配。switch语句的 case 中匹配的值可以声明为临时常量或变量,在 case 作用域内使用,也可以配合where来描述更复杂的匹配条件。

    Switch

    区间匹配

    case 分支的模式也可以是一个值的区间。

    元组

    我们可以使用元组在同一个switch语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(_)来匹配所有可能的值。

    值绑定(Value Bindings)

    case 分支允许将匹配的值声明为临时常量或变量,并且在case分支体内使用 —— 这种行为被称为值绑定(value binding)。

    case 分支的模式可以使用where语句来判断额外的条件。

    复合匹配

    当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个case后面,并且用逗号隔开。

    控制转移语句

    控制转移语句改变代码的执行顺序,通过它可以实现代码的跳转。

    Swift有五种控制转移语句:

    • continue
    • break
    • fallthrough
    • return
    • throw

    在 Swift 里,switch语句不会从上一个 case 分支跳转到下一个 case 分支中。

    相比之下,C 语言要求你显式地插入break语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。

    如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用fallthrough关键字。

    可以使用标签(statement label)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用break加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用break或者continue加标签,来结束或者继续这条被标记语句的执行。

    例如,针对循环体的标签:

    label name: while condition { statements }

    提前退出

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

    guard let/var condition else {
        return // break/continue/throw/fatalError()
    }

    相比于可以实现同样功能的if语句,按需使用guard语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在else块中,它可以使你在紧邻条件判断的地方,处理违规的情况。

    检测API可用性

    if #available(platform name version, ..., *) {
        APIs 可用,语句将执行
    } else {
        APIs 不可用,语句将不执行
    }

    6. 函数(Functions)


    1. The Swift Programming Language http://wiki.jikexueyuan.com/project/swift/
    展开全文
  • 希望对Swift的学习者有所帮助,使用的编写工具:JQNote 常量和变量关联一个名称(比如:maximumNumberOfLoginAttempts 和 welcomeMessage)和一个特殊类型的值(比如:数字10和字符串“Hello”)。一个常量一旦...
  • [swift]4.0原生字符串

    2017-03-05 15:40:42
    swift4.0原生字符串
  • Swift关键字总结上篇

    2019-06-20 11:11:21
    Swift 中有多少关键字? 在Swift官方文档的词汇结构中, 有非常多的关键字, 它们被用于声明中、语句中、表达式中、类中、模式中, 还有以数字符号#开头的关键字, 以及特定上下文环境使用的关键字。 本文中涉及的代码...
  • Swift4.0入门视频教程,课程内容包含Swift入门知识、数据类型(元组、Dictionary、运算符)、流程控制、函数、类与对象、扩展协议、常见概念、实用进阶、案例实战。 1、119节大容量课程:包含了Swift4.0语言大部分...
  • final 关键字 很多语言都有final这个关键字Swift中,这个关键字可以用来修饰 class、func、var。表示被修饰的对象无法被继承。
  • swift 关键词
  • Swift 4.0 新特性

    2017-08-15 19:37:46
    WWDC 2017 带来了很多惊喜,在这次大会上,Swift 4 也伴随着 Xcode 9 测试版来到了我们的面前,虽然正式版要8月底9月初才会公布,但很多强大的新特性正吸引我们去学习它。根据大会上已经开放的新特性,先一睹为快。...
  • swift中闭包是一个很强大的东西,闭包是自包含的函数代码块,可以在代码中被传递和使用。跟C 和 Objective-C 中的代码块(blocks)很相似 。这个大家必须掌握!必须掌握!必须掌握!重要的事情要说三遍
  • Swift、Xcode和Cocoa入门指南(iOS9 Programming Fundamentals With swift)  第二章 函数  Swift语法中最具特色也最重要的就是声明和调用函数的方式;  所有代码都位于函数中,而动作则是由函数触发的;  ...
  • 素材:Language Guide初次接触 Swift,建议先看下 A Swift Tour ,否则思维转换会很费力,容易卡死或钻牛角尖。同样是每一章只总结3个自己认为最重要的点。这样挺好!强迫你去思考去取舍。以后再看,也方便快速重建...
  • Swift3 单例模式

    2017-06-14 10:05:06
    常见的有这么几种方法 第一种简单到爆的 final class Single: NSObject { static let shared = Single() ...final关键字的作用是这个类或方法不希望被继承和重写 第二种 public extension DispatchQueue { private s
  • 所以慢慢多了很多OC混编Swift的项目,但是其实swift调用OC还是挺简单的,把OC项目慢慢转成swift问题不大。swift可以算是OC更上层的语言,我这里说的坑都是关于Swift转OC的,很奇葩吧,还有把Swift项目转成OC的,一句...
  • Swift4.0 访问权限

    2018-02-09 14:54:36
    Swift 4.0访问权限关键字 目前有 open public internal fileprivate private 这五个访问权限,都可以修饰属性和方法。下面通过例子来分析五个访问权限的使用。不同点分析:是否可以被子类继承访问同文件不同类下...
  • 使用 mutating 关键字修饰方法是为了能在该方法中修改 struct 或是 enum 的变量,在设计接口的时候,也要考虑到使用者程序的扩展性。所以要多考虑使用mutating来修饰方法。 struct SimpleStruct: ...
  • 用Xcode8打开自己的Swift2.3的项目,选择Edit->Convert->To Current Swift Syntax… 让Xcode帮我们把Swift2.3的代码转换为Swift3.0。 手动调出Xcode自动转换Swift2.3 到 Swift3.0弹出语言版本选择界面,选择Covert ...
  • swift - 类型中的self

    2016-04-27 15:27:54
    swift中,关键字表示类型本身 。 (self) 意义 : 消除局部和类型全局的歧义。 class Counter{ var count = 0  func increment(){  self.count++ } }
1 2 3 4 5 ... 20
收藏数 1,083
精华内容 433
关键字:

4.0关键字 swift