zoukankan      html  css  js  c++  java
  • 从0开始学Swift笔记整理(二)

    这是跟在上一篇博文后续内容:

    ——函数中参数的传递引用

    类是引用类型,其他的数据类型如整型、浮点型、布尔型、字符、字符串、元组、集合、枚举和结构体全部是值类型。

    有的时候就是要将一个值类型参数以引用方式传递,这也是可以实现的,Swift提供的inout关键字就可以实现。看下面的一个示例:

    func increment(inout value:Double, amount:Double = 1.0) {

    value += amount

    }

    var value : Double = 10.0

    increment(&value)

    print(value)

    increment(&value, amount:100.0)

    print(value)

    代码increment(&value)是调用函数increment,增长量是默认值,其中&value(在变量前面加&符号,取出value地址)是传递引用方式,它在定义函数时,参数标识与inout是相互对应的。

    代码increment(&value, amount:100.0)也是调用函数increment,增长量是100.0。

    上述代码输出结果如下:

    11.0

    111.0

    ——函数返回值

    返回值3种形式:无返回值、单一返回值和多返回值。

    无返回值函数

    所谓无返回结果,事实上是Void类型,即表示没有数据的类型。

    无返回值函数的语法格式有如下3种形式:

    func 函数名(参数列表)  {

    语句组

    }

    func 函数名(参数列表) ->() {

    语句组

    }

    func 函数名(参数列表) ->Void {

    语句组

    }

    无返回值函数不需要“return返回值”语句。

    多返回值函数

    两种方式来实现。

    一种是在函数定义的时候,将函数的多个参数声明为引用类型传递,这样当函数调用结束时,这些参数的值就变化了。

    另一种是将返回定义为元组类型。

    介绍元组类型返回多值的实现。看一个示例:

    func position(dt: Double, speed:(x:Int, y:Int)) -> (x:Int, y:Int) {

    let posx:Int = speed.x * Int(dt)

    let posy:Int = speed.y * Int(dt)

    return (posx, posy)

    }

    let move = position(60.0, speed: (10, -5))

    print("物体位移:(move.x) , (move.y)")

    参数speed:(x:Int, y:Int)是元组类型。

    position函数的返回值是(x:Int, y:Int)的元组类型。

    代码调用函数,传递的时期间是60.0秒,速度是(10, -5)。

    输出结果,结果如下:

    物体位移:600 , -300

    ——闭包

    闭包是自包含的匿名函数代码块,可以作为表达式、函数参数和函数返回值,闭包表达式的运算结果是一种函数类型。
    Swift中的闭包类似于Objective-C 中的代码块、Java中的匿名内部类。
    Swift中的闭包表达式很灵活,其标准语法格式如下:
    { (参数列表) ->返回值类型 in
    语句组
    }
    其中,参数列表与函数中的参数列表形式一样,返回值类型类似于函数中的返回值类型,但不同的是后面有in关键字。

    Swift提供了多种闭包简化写法,我来介绍下面几种不同形式:

    1、类型推断简化
    类型推断是Swift的强项,Swift可以根据上下文环境推断出参数类型和返回值类型。以下代码是标准形式的闭包:

    {(a:Int, b:Int) -> Int in
    return a + b
    }

    Swift能推断出参数a和b是Int类型,返回值也是Int类型。简化如下:

    {(a, b) in return a + b }    
    {a, b
    in return a + b }    //参数列表括号也可以省略

    2、隐藏return关键字
    在闭包内部语句组只有一条语句,如return a + b等,那么这种语句都是返回语句。前面的关键字return可以省略,省略形式如下:
    {a, b in a + b }

    func calculate(opr :String)-> (Int,Int)-> Int {
    var result : (Int,Int)-> Int
         switch (opr) {
              
    case "+" :
               result = {a, b
    in a + b } //return关键字省略了
              
    default:
               result = {a, b
    in a - b } //return关键字省略了
        }
        return result
    }

    省略的前提是闭包中只有一条return语句。

    3、省略参数名称
    Swift提供了参数名省略功能,我们可以用$0、$1、$2…来指定闭包中参数,$0指代第一个参数,$1指代第二个参数,$2指代第三个参数,以此类推$n+1指代第n个参数。
    使用参数名省略功能,在闭包中必须省略参数列表定义,Swift能够推断出这些缩写参数的类型。参数列表省略了,in关键字也需要省略。参数名省略之后如下所示:{$0 + $1}

    func calculate(opr :String)-> (Int,Int)-> Int {
    var result : (Int,Int)-> Int
      
    switch (opr) {
          case "+" :
          result = {$
    0 + $1}    //采用了参数名省略
          
    default:
          result = {$
    0 - $1}    //采用了参数名省略
       }
    return result
    }
    let f1:(Int,Int)-> Int = calculate("+")
    print("10 + 5 = (f1(10,5))")
    let f2:(Int,Int)-> Int = calculate("-")
    print("10 - 5 = (f2(10,5))")

    4、使用闭包返回值
    闭包表达本质上是函数类型,是有返回值的,我们可以直接在表达式中使用闭包的返回值。重新修改add和sub闭包,示例代码如下:

    let c1:Int = {(a:Int, b:Int) -> Int in
    return a + b
    }(
    10,5)  
    print("10 + 5 = (c1)")

    解释:给c1赋值,后面是一个闭包表达式。但是闭包表达式不能直接赋值给c1,因为c1是Int类型,需要闭包的返回值。这就需要在闭包结尾的大括号后面接一对小括号(10,5),通过小括号(10,5)为闭包传递参数。

    ——Swift尾随闭包

    闭包表达式可以作为函数的参数传递,如果闭包表达式很长,就会影响程序的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

    下面我们来看一个示例代码:

    func calculate(opr: String, funN:(Int, Int) -> Int) {

    //最后一个参数funN是(Int,Int)-> Int函数类型,funN可以接收闭包表达式

    switch (opr) {

    case "+" :

    print("10 + 5 = (funN(10,5))")

    default:

    print("10 - 5 = (funN(10,5))")

    }

    }

    calculate("+", funN: {(a: Int, b: Int) -> Int in return a + b }) //调用
    calculate("+"){(a: Int, b: Int) -> Int in return a + b } //调用,这种形式就是尾随闭包
    calculate("+") { $0 + $1 } //调用,这种形式就是尾随闭包

    需要注意的是,闭包必须是参数列表的最后一个参数,如果calculate函数采用如下形式定义:

    func calculate(funN:(Int, Int) -> Int, opr:String) {

    ...

    }

    由于闭包表达式不是最后一个参数,那么调用calculate函数就不能使用尾随闭包写法的。

    ——枚举(enum)

    Swift中的枚举可以定义一组常量、提高程序的可读性;还具有面向对象特性。

    使用enum关键词声明枚举类型,具体定义放在一对大括号内,枚举的语法格式如下:

    enum 枚举名

    {

       枚举的定义

    }

    “枚举名”是该枚举类型的名称。它首先应该是有效的标识符,其次应该遵守面向对象的命名规范,它由一组成员值和一组相关值组成。

    成员值

    枚举的成员值默认情况下不是整数类型,以下代码是声明枚举示例:

    enum WeekDays {

        case Monday

        case Tuesday

        case Wednesday

        case Thursday

        case Friday

    }

    在这些成员值前面要加上case关键字,也可以将多个成员值放在同一行,用逗号隔开,如下所示:

    enum WeekDays {

        case Monday, Tuesday, Wednesday, Thursday, Friday

    }

    下面我们看一个示例,代码如下:

    var day = WeekDays.Friday

    day = WeekDays.Wednesday

    day = .Monday          

    使用枚举成员赋值时候,我们可以采用完整的“枚举类型名.成员值”的形式,也可以省略枚举类型采用“.成员值”的形式。这种省略形式能够访问的前提是,Swift编译器能够根据上下文环境推断类型。因为我们已经在第1行和第2行给day变量赋值,所以即使第3行代码采用缩写,Swift编译器能够推断出数据类型是WeekDays。

    原始值

    出于业务上的需要,要为每个成员提供某种基本数据类型,我们可以为枚举类型提供原始值(raw values)声明,这些原始值类型可以是:字符、字符串、整数和浮点数等。

    原始值枚举的语法格式如下:

    enum 枚举名: 数据类型

    {   

       case 成员名 = 默认值

       ...

    }

    在“枚举名”后面跟“:”和“数据类型”就可以声明原始值枚举的类型,然后在定义case成员的时候需要提供原始值。

    以下代码是声明枚举示例:

    enum WeekDays: Int {

        case Monday = 0

        case Tuesday = 1

        case Wednesday = 2

        case Thursday = 3

        case Friday = 4

    }

    我们声明的WeekDays枚举类型的原始值类型是Int,需要给每个成员赋值,只要是Int类型都可以,但是每个分支不能重复。

    相关值

    在Swift中除了可以定义一组成员值,还可以定义一组相关值(associated values),它有点类似于C中的联合类型。下面看一个枚举类型的声明:

    enum Figure {

        case Rectangle(Int, Int)

        case Circle(Int)

    }

    枚举类型Figure(图形)有两个相关值: Rectangle(矩形)和Circle(圆形)。Rectangle和Circle是与Figure有关联的相关值,它们都是元组类型,对于一个特定的Figure实例,只能是其中一个相关值。从这一点来看,枚举类型的相关值类似于C中的联合类型。

    ——类和结构体定义

    Swift中的类和结构体定义的语法是非常相似的。类使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下:

    class 类名 {

    定义类的成员

    }

    struct 结构体名 {

    定义结构体的成员

    }

    从语法格式上看,Swift中的类和结构体的定义更类似于Java语法,不需要像C++和Objective-C那样把接口部分和实现部分放到不同的文件中。

    下面来看一个示例:

    class Employee { //定义的类

    var no: Int = 0

    var name: String = ""

    var job: String?

    var salary: Double = 0

    var dept: Department?

    }

    struct Department { //定义的结构体

    var no: Int = 0

    var name: String = ""

    }

    里面定义了一些属性。

    Employee和Department是有关联关系的。

    下列语句实例化:

    let emp = Employee()

    var dept = Department()

    Employee()和Department()是调用它们的构造函数实现实例化。

    注意:类声明为let常量还是var变量呢?从编程过程讲类一般声明为let常量,由于类是引用数据类型,声明为let常量只是说明不能修改引用,但是引用指向的对象可以被修改。

    ——可选链

    可选链与类图之间是典型的关联关系类图。这些类一般都是实体类,实体类是系统中的人、事、物。Employee通过dept属性与Department关联,Department通过comp属性与Company关联。

    下面看示例代码:

    class Employee {

    var no:Int = 0

    var name:String = "Tony"

    var job:String?

    var salary:Double = 0

    var dept:Department = Department()

    }

    class Department {

    var no:Int = 10

    var name:String = "SALES"

    var comp: Company = Company()

    }

    class Company {

    var no:Int = 1000

    var name:String = "EOrient"

    }

    letemp = Employee()  //Employee实例

    print(emp.dept.comp.name)   //emp.dept.comp.name可以引用到Company实例,形成一个引用的链条,但是这个“链条”任何一个环节“断裂”都无法引用到最后的目标(Company例)。

    给定一个Employee实例,一定会有一个Department与其关联。但现实是一个新入职员工未必有部门,这种关联关系有可能有值,也有可能没有值,我们需要使用可选类型(Department?)声明dept属性。

    修改代码如下:

    class Employee {

    var no:Int = 0

    var name:String = "Tony"

    var job:String?

    var salary:Double = 0

    var dept:Department?//= Department()

    }

    class Department {

    var no:Int = 10

    var name:String = "SALES"

    var comp:Company?//=

    Company()

    }

    class Company {

    var no:Int = 1000

    var name:String = "EOrient"

    }

    let emp = Employee()

    print(emp.dept!.comp!.name)//显示拆包

    print(emp.dept?.comp?.name)//可选链

    其中可选类型的引用,可以使用感叹号(!)进行显示拆包,代码修改如下:

    print(emp.dept!.comp!.name)

    但是显示拆包有一个弊端,如果可选链中某个环节为nil,将会导致代码运行时错误。我们可以采用更加“温柔”的引用方式,使用问号(?)来代替原来感叹号(!)的位置,如下所示:

    print(emp.dept?.comp?.name)

    ——可选类型

    可选类型:

    var n1: Int = 10
    n1 = nil //编译错误
    let str: String = nil //编译错误

    Int和String类型不能接受nil的,但程序运行过程中有时被复制给nil是在所难免的,Swift为每一种数据类型提供一种可选类型(optional),即在某个数据类型后面加上问号(?)或感叹号(!),修改前文示例代码:

    var n1: Int? = 10
    n1 = nil
    let str: String! = nil

    Int?和String!都是原有类型Int和String可选类型,它们可以接受nil。

    可选类型值拆包

    在可选类型的问号(?)或感叹号(!)究竟有什么区别呢?这与可选类型的“拆包”(unwrapping)有关,拆包是将可选类型变成普通类型,如果我们直接打印非空的可选类型值,代码如下:

    var n1: Int? = 10
    print(n1)

    输出的结果是Optional(10),而非10。所以试图计算表达式n1 + 100会发生编译错误,代码如下:

    var n1: Int? = 10
    print(n1 + 100) //发生编译错误

    需要对可选类型值进行“拆包”是必要地。

    “拆包”分为显示拆包和隐性拆包。

    使用问号(?)声明的可选类型,在拆包时需要使用感叹号(!),这种拆包方式称为“显式拆包”;

    使用感叹号(!)声明的可选类型,在拆包时可以不使用感叹号(!),这种表示方式称为“隐式拆包”。

    看看下面代码:

    var n1: Int? = 10
    print(n1! + 100) //显式拆包
    var n2: Int! = 100
    print(n2 + 200) //隐式拆包

    总结使用问号(?)和感叹号(!)

    在使用可选类型和可选链时,多次使用了问号(?)和感叹号(!),但是它们的含义是不同的,下面我来详细说明一下。

    1. 可选类型中的问号(?)

    声明这个类型是可选类型,访问这种类型的变量或常量时要使用感叹号(!),下列代码是显示拆包:

    let result1: Double? = divide(100, 200)
    print(result1!)

    2. 可选类型中的感叹号(!)

    声明这个类型也是可选类型,但是访问这种类型的变量或常量时可以不使用感叹号(!),下列代码是隐式拆包:

    let result3: Double! = divide(100, 200)
    print(result3)

    3. 可选链中的感叹号(!)

    多个实例具有关联关系,当从一个实例引用另外实例的方法、属性和下标等成员时就会形成引用链,由于这个“链条”某些环节可能有值,也可能没有值,因此需要采用如下方式访问:

    emp.dept!.comp!.name

    4. 可选链中的问号(?)

    在可选链中使用感叹号(!)访问时,一旦“链条”某些环节没有值,程序就会发生异常,我们把感叹号(!)改为问号(?),代码如下所示:

    emp.dept?.comp?.name

    这样某些环节没有值的时候返回nil,程序不会发生异常。

    ——访问级别

    Swift提供了3种不同访问级别,对应的访问修饰符为:public、internal和private。这些访问修饰符可以修饰类、结构体、枚举等面向对象的类型,还可以修饰变量、常量、下标、元组、函数、属性等内容。

    · public。可以访问自己模块中的任何public实体。如果使用import语句引入其他模块,我们可以访问其他模块中的public实体。

    · internal。只能访问自己模块的任何internal实体,不能访问其他模块中的internal实体。internal可以省略,换句话说,默认访问限定是internal。

    · private。只能在当前源文件中使用的实体,称为私有实体。使用private修饰,可以用作隐藏某些功能的实现细节。

    使用访问修饰符的示例代码如下:

    public class PublicClass {}
    internal class InternalClass {}
    private class PrivateClass {}
    public var intPublicVariable = 0
    let intInternalConstant = 0
    private func intPrivateFunction() {}

    使用最佳访问级别:

    由于中Swift中访问限定符能够修饰的实体很多,使用起来比较繁琐,下面我们给出一些最佳实践。

    1. 统一性原则

    原则1:如果一个类型(类、结构体、枚举)定义为internal或private,那么类型声明的变量或常量不能使用public访问级别。因为public的变量或常量可以被任何人访问,而internal或private的类型不可以。

    原则2:函数的访问级别不能高于它的参数和返回类型(类、结构体、枚举)的访问级别。假设函数声明为public级别,而参数或者返回类型声明为internal或private,就会出现函数可以被任何人访问,而它的参数和返回类型不可以访问的矛盾情况。

    2. 设计原则

    如果我们编写的是应用程序,应用程序包中的所有Swift文件和其中定义的实体,都是供本应用使用的,而不是提供其他模块使用,那么我们就不用设置访问级别了,即使用默认的访问级别。

    如果我们开发的是框架,框架编译的文件不能独立运行,因此它天生就是给别人使用的,这种情况下我们要详细设计其中的Swift文件和实体的访问级别,让别人使用的可以设定为public,不想让别人看到的可以设定为internal或private。

    3. 元组类型的访问级别

    元组类型的访问级别遵循元组中字段最低级的访问级别,例如下面的代码:

    private class Employee {

    var no: Int = 0

    var name: String = ""

    var job: String?

    var salary: Double = 0

    var dept: Department?

    }

    struct Department {

    var no: Int = 0

    var name: String = ""

    }

    private let emp = Employee()
    var dept = Department()
    private var student1 = (dept, emp)

    4. 枚举类型的访问级别

    枚举中成员的访问级别继承自该枚举,因此我们不能为枚举中的成员指定访问级别。示例代码如下:

    public enum WeekDays {

    case Monday

    case Tuesday

    case Wednesday

    case Thursday

    case Friday

    }

    由于WeekDays枚举类型是public访问级别,因而它的成员也是public级别。

    ——选择类结构体的关系

    类和结构体非常相似,很多情况下没有区别。类和结构体异同:
    类和结构体都有如下功能
    · 定义存储属性
    · 定义方法
    · 定义下标
    · 定义构造函数
    · 定义扩展
    · 实现协议
    只有类才有的功能
    · 能够继承另外一个类
    · 能够核对运行期对象的类型
    · 析构对象释放资源
    · 引用计数允许一个实例有多个引用
    选择的原则:
    结构体是值类型,每一个实例没有独一无二的标识,下面两个数组实例本质上没有区别,他们可以互相替换。
    var studentList1: [String] =  ["张三","李四","王五"]
    var studentList2: [String] =  ["张三","李四","王五"]
    但是我们提到类时候,它是引用类型,每个实例都有独一无二的标识。我们看看下面员工Employee类代码:
    class Employee { 
        var no = 0  
        var name = "" 
        var job = ""  
        var salary = 0.0
    }
    var emp1 = Employee()
    emp1.no = 100
    emp1.name = "Tom"
    emp1.job = "SALES"
    emp1.salary = 9000
    var emp2 = Employee()
    emp2.no = 100
    emp2.name = "Tom"
    emp2.job = "SALES"
    emp2.salary = 9000
    emp1和emp2两个员工实例即便内容完全相同,但是这不能说明他们就是同一个员工,只是相似而已。每一个员工实例的背后都有独一无二的标识。
    我们再来看看部门Department结构体。
    struct Department {
        var no: Int = 0
        var name: String = ""
    }
    var dept1 = Department()
    dept1.no = 20
    dept1.name = "Research"
    var dept2 = Department()
    dept2.no = 20
    dept2.name = "Research"
    Department为什么被设计成为结构体而不是类呢,那要看我们对于两个不同部门的理解是什么,如果具有相同的部门编号(no)和部门名称(name),我们就认为他们是两个相同的部门,那么就可以把Department定义为结构体,这一点与员工Employee不同。

    这是我在学Swift整理的基础笔记,希望给更多刚学IOS开发者带来帮助,在这里博主非常感谢大家的支持!

    更多的请到参考我下一篇博文。之后还在持续更新中。。。

  • 相关阅读:
    tyvj1061Mobile Service
    POJ3666序列最小差值
    POJ2279杨氏矩阵+钩子定理
    POJ2127 LICS模板
    codevs2189数字三角形(%100)
    qhfl-7 结算中心
    qhfl-6 购物车
    qhfl-5 redis 简单操作
    qhfl-4 注册-登录-认证
    qhfl-3 Course模块
  • 原文地址:https://www.cnblogs.com/ZRJ-boke/p/6103959.html
Copyright © 2011-2022 走看看