Swift学习笔记(15)—— 析构过程

Swift会自动释放不再使用的实例,所以通常类实例不再使用的时候也不需要手动清理。但是有时候可能需要一些额外的清理,这些额外的清理就可以写到析构函数中——析构函数会在实例被调用之前被自动调用。
每个类只能有一个析构函数,用deinit定义,并且不带参数,不带括号:

deinit{
    //执行析构过程
}

* 析构函数在实例被释放前自动调用。
* 析构函数只能被自动调用,不能手动调用。
* 析构函数会被继承,子类如果也有显示的析构函数,会在其末尾自动调用父类的析构函数。

    class MyClass {
        var name :String? = "myClass"
        func say(){
            println("My name is \(name)");
        }
        init(name :String){
            self.name = name
        }
        deinit{
            println("MyClass回收\(name)");
        }
    }

    class MyClass2:MyClass{
        deinit{
            println("MyClass2回收\(name)")
        }
    }

    var c:MyClass2? = MyClass2(name:"number1")
    c!.say()
    c = nil  //这里会回收c,回收之前抵用析构函数

以上代码输出:

My name is number1
MyClass2回收number1
MyClass回收number1



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/352

Swift学习笔记(14)—— 构造过程

跟JAVA类的构造过程有差异,这点看懂了对以后估计会很有帮助。


14.构造过程

使用某个类、结构体或者枚举类型的实例之前进行的准备过程。

存储型属性的初始化赋值

** 存储型属性(可选类型除外)的初始化赋值是必须的,可以在定义的时候赋值(默认值)或者在构造器中赋值。**
注意:初始化赋值不会触发属性观察器(willset、didset等)。
——这样,能保证一个类(结构体、枚举类型)的实例在创建的时候对每一个成员变量进行初始化。

定制化构造过程

构造参数

可以定个多个相同类型参数的构造器,只要参数名称不同即可。

class Time {
    var time :Int
    init(second : Int){  //构造器1
        time = second
    }
    init(m minute : Int){  //构造器2,跟构造器1参数名不同,自定义外部参数名  
        time = minute * 60
    }
    init(hour :Int){      //构造器3
        time = hour * 3600
    }
    init(hour :String){  //构造器4,跟构造器3参数类型不同
        time = 60
    }
}

Swift是用参数名、参数类型、参数个数来区分不同的构造器的——比JAVA、C多了一个参数名——所以调用构造器的时候,参数名是必须的。
跟函数一样,可以自己指定一个外部参数名,如果没有指定,构造器的参数名将默认作为外部参数名。

var t1 = Time(second:40)    //会调用构造器1
var t2 = Time(m:2)          //会调用构造器2
var t3 = Time(hour:1)       //会调用构造器3
var t4 = Time(hour:"1")     //会调用构造器4 
var t5 = TIme(3)            //编译报错,因为没有指定参数名

可选类型属性

可以不需要显式进行初始化,因为会在定义的时候自动初始化为nil了,这对可选类型来说已经是一个正确的值了。

修改常量的值

**只要能在构造过程完成之前,常量的值能够确定,常量的值可以在构造过程中任意修改

默认构造器

Swift为结构体和类提供了默认的构造器(无参

需要类或结构体满足以下条件:

  • 没有其他任何构造器。
  • 所以的属性已经在定义的时候提供了默认值。

    class ShoppingListItem {
        var name: String?
        var quantity = 1
        var purchased = false
    }
    var item = ShoppingListItem()//默认构造器
    

结构体的逐一构造器

struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

值类型的构造器代理

在一个构造器中调用另一个构造器——跟JAVA类似。
在一个构造器中使用self.init调用另一个构造器,self.init也只能在构造器中调用。

类的继承和构造过程

指定构造器和便利构造器

  • 指定构造器: 正常的构造器,对参数进行直接初始化的构造器
  • 便利构造器: 调用了其他构造器的构造器……使用关键字convenience标识

构造器链的规则:

  1. 指定构造器必须调用其直接父类的的指定构造器。
  2. 便利构造器必须调用同一类中定义的其它构造器。
  3. 便利构造器必须最终以调用一个指定构造器结束。

一个更方便记忆的方法是:

  • 指定构造器必须总是向上代理
  • 便利构造器必须总是横向代理

图示

两段式构造过程

阶段一

顺着构造器链,自底向上,直至到达构造器链的顶部,即基类。并且确保所有实例的存储值类型都已经赋值。阶段一完成。
构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self的值。

阶段二

自顶向下,直至到达最开始调用构造的子类,中间的每一层类都有一次定制实例的机会。(每一次向下一层的类都可以覆盖上层的定制),直到最终子类的指定构造器完成,最终子类的便利构造器可以执行最终的定制,进行更多的操作。
构造器此时可以访问self、修改它的属性并调用实例方法等等。

构造器的继承和重载

跟JAVA不同,Swift的子类不会默认继承父类的构造器。
可以使用override关键字对父类相同的构造器进行重载。

自动构造器的继承

上面说不会默认继承——在满足指定条件的时候是可以继承构造器的,条件:

  1. 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
  2. 如果子类提供了所有父类指定构造器的实现–不管是通过规则1继承过来的,还是通过自定义实现的–它将自动继承所有父类的便利构造器。

用闭包和函数给属性值初始化

语法:

class SomeClass {
    let someProperty: SomeType = {
        // 在这个闭包中给 someProperty 创建一个默认值
        // someValue 必须和 SomeType 类型相同
        return someValue
    }() //注意这里的括号是必须的。
}

栗子:

class Time {
    var someValue  : Int = {
        return 3;
    }()
}


这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/270

Swift学习笔记(13)—— 继承

13.继承

基类

跟JAVA不一样,Swift没有一个类似Object的公共基类,如果没有显示给一个类声明一个父类,那么它就是基类,不继承自任何类。

构造器

  • 类中的属性如果没有在声明的时候初始化,那么就必须有构造器,而且构造器中必须对所有属性进行初始化,否则会编译不通过。
  • 构造器一律用init关键字标识——而非JAVA中的用类名作为构造函数——但是调用构造的时候要用类名!
  • 构造器可以有多个,只要参数不同即可。

    class People {
        //如果这两个属性在这里进行初始化了,则下面的两个构造器就不是必须的了。  
        var name : String 
        var age  : Int
    
        init(){
            name = "NO NAME"
            age = 10
        }
    
        init(name:String ,age:Int){
            self.name = name
            self.age  = age
        }
    }
    
    //构造类
    var p1: People = People()
    var p2: People = People(name: "Barry",age: 24)
    

继承

语法

class Barry :People {
    init(){
        super.init()
        name = "Barry"
        age = 24
    }
}

var p3: Barry = Barry()

子类只能修改从父类继承的变量属性,不能修改常量

如上面的Barry类,修改从父类People类继承的name和age属性—— 如果name和age是常量的话,就不能修改了。

重写

重写方法

重写父类的方法,需要在前面加上override关键字。

访问父类的方法、属性、下标脚本

方法: super.someFunction()
属性: super.someProperty
下标脚本: super[index]

class People {
    var name : String
    var age  : Int
    init(){
        name = "NO NAME"
        age = 10
    }
    func say() -> Void {
        println("My Name is \(name), I am \(age) yeas old.");
    }
}

class Barry :People {
    init(){
        super.init()
        name = "Barry"
        age = 24
    }
    // 重写父类方法
    override func say(){
        println("Barry Says : \"I am \(age) yeas old! \"")
        super.say() // 调用父类的方法
    }
}

var p1: People = People()
p1.say()
var p3: Barry = Barry()
p3.say()
/* 输出结果:
    My Name is NO NAME, I am 10 yeas old.
    Barry Says : "I am 24 yeas old! "
    My Name is Barry, I am 24 yeas old. */

重写属性

同样是override关键字,可以重写从父类继承的属性的get、set、willset、didset等。

防止重写

嗯,final关键字——跟JAVA差不多。
可以加到方法名、属性名、下表脚本名、类名等前面,使之不能被重写(类不能被继承)。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/268

Swift学习笔记(12)—— 下标脚本

呼呼,上周好忙,一直没时间,今天开始继续学习……


12.下标脚本

就是类似于数组下标、字典下标的实现方式。

语法

在类、结构体、枚举了类型中,用subscript关键字定义。

class C {

    var a : Int = 10
    var b : Int = 20

    subscript (index: Int) -> Int{
        set(newValue){
            if( index % 2 == 1){
                a = index
            }else{
                b = index
            }
        }
        get{
            if( index % 2 == 1){
                return a
            }else{
                return b
            }
        }
    }

    //如果只有get没有set时,可以把get关键字省去,直接把语句写到subscript里面。  
    subscript (index: String) -> String{
        return index+index
    }
}

var c = C()
println(c[0])           //20
println(c[1])           //10
println(c[10011])       //10
println(c[10086])       //20

跟属性的定义类似啊。

脚本的下标可以是多个

比如a[2,1]这样的形式,在定义脚本的时候要多个参数即可。

可以定义多个脚本

只要脚本的参数类型或个数不同即可。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/266

Swift学习笔记(11)—— 方法

11.方法

方法就是与默写特定类型(类、结构体、枚举类)相关联的函数。

结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。

实例方法

就是在类、结构体、枚举类中的一般方法。

方法的局部参数名称和外部参数名称

实例方法的第一个参数名称默认为局部参数名称(调用的时候不需要写参数名);其他的参数名称默认为局部参数名称和外部参数名称——(外部参数名称调用的时候需要显式指定)。

class C {
    var number :Int = 1
    func addTo(newNumber :Int , number2:Int) {
        number += (newNumber + number2)
    }
}

var c = C()
println(c.number) //1
c.addTo(4,number2:5) // number2 参数名称需要显式指定
println(c.number) //10

当然这是默认情况,可以像其他方法一样自行更改内部外部参数名的其他方式。

self属性

大概相当于是JAVA类中的this吧。

变异方法

一般情况下,值类型的属性不能在实例方法中被修改。(结构体和枚举都是值类型)
如果想在实例方法中修改值类型的属性,需要用mutating关键字标识方法为变异的

struct C { // 注意跟上面的 class C 区别,这里用了struct,是一个结构体
    var number :Int = 1
    // 因为addTo方法修改了属性值,所以这里必须私用"mutating"关键词,否则会报错
    mutating func addTo(newNumber :Int , number2:Int) {
        number += (newNumber + number2)
    }
}

变异方法中可以修改self的值,使其本身值改变。

类型方法

就相当于JAVA的静态方法。
使用static关键字标识。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/262

Swift学习笔记(10)—— 属性

10.属性

存储属性

就是一般的正常属性了,用var或者let定义。

常量和存储属性

如果把结构体的实例赋值给一个常量,则实例的属性不能再修改了;但如果是一个类的实例赋值给一个常量,实例的变量属性依然可以修改——因为结构体是值类型,类是引用类型。

延迟存储属性

使用@lazy标识一个延迟存储属性。
延迟属性在第一次调用的时候才会被初始化——目测适用于一些初始化比较耗时的属性。
因为常量属性在构造过程完成之前必须要有初始值,所以常量属性无法被标识为延迟属性。

计算属性

就是非直接的属性,而是计算出来的属性——比如一个圆的半径是一个存储属性,而周长是可以用半径计算出来的,则就可以将周长声明为一个计算属性,这样当改变半径的时候,周长也会跟着自动改变,也可以通过方法在设置周长的时候自动调整属性。

一般形式:一个getter,一个可选的setter

struct Circle {
    var radius : Double = 1 //半径
    var length :Double { //周长
    get{
        return 3.14 * 2 * radius
    }
    set(newLength){
        radius = newLength / (3.14 * 2)
    }
    }
}

var c = Circle()
println(c.length)  // 输出6.28  (调用get)
c.length = 12.56    // (调用set,会改变radius的值)
println(c.radius)   // 输出2.0

TIP: getter是必须的,setter是可选的

便捷setter声明

可以省略setter的参数名,这样可以在setter内部默认使用newValue作为参数名。
上面的set可以简写成这样:

set{
    radius = newValue / (3.14 * 2)
}

只读计算属性

只写getter不写setter就行了……
只读属性可以省略get关键字和花括号。

struct Circle {
    var radius : Double = 1 //半径
    var length :Double { //周长
        return 3.14 * 2 * radius
    }
}

属性监视器

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

  • willSet在设置新的值之前调用
    传入新的属性值作为参数。
    可以省略参数名,默认使用newValue
  • didSet在新的值被设置之后立即调用
    传入旧的属性值作为参数。
    可以省略参数名,默认使用oldValue

    struct Circle {
        var radius : Double = 1{//半径
        willSet(newValue){
            println("radius将要设置成\(newValue)")
        }
        didSet{
            println("radius已经重新设定,旧的值是\(oldValue),新的值是\(radius)")
        }
    
    }
    var c = Circle()
    c.radius = 20
    
    //输出结果:
    //  radius将要设置成20.0
    //  radius已经重新设定,旧的值是1.0,新的值是20.0
    

全部变量和局部变量

全部属性都是延迟属性,即都会在调用的时候才进行初始化,而且不需要@lazy关键字。

类型属性

可以直接用类型名(结构体名、类名等)调用的属性——目测就是JAVA的静态属性。
声明时加上static关键字即可。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/260

Swift学习笔记(9)—— 类和结构体

9.类和结构体

定义方法

跟C/JAVA基本一样。
类使用class关键字,结构体使用struct关键字。

实例

是java差不多,不过不需要new这个词。

var myClass = MyClass()
let myStruct = MyStruct()

属性访问

嗯,使用点.

myClass.value1

可以用这个方法获取值/修改值。

结构体类型的逐一构造器

所有的结构体都默认会有的一个构造器。
比如

struct MyStruct{
    var a = 0
    var b = 0
}

//使用逐一构造器
let myStruct = MyStruct(a:20,b:30);

类没有默认的逐一构造器!!!

结构体和枚举是值类型 & 类是引用类型

结构体和枚举是值类型

——在代码传递的时候会被复制。

类是引用类型

——在代码传递的时候默认是地址引用。
(相当于C语言中的指针)

恒等运算符

  • === 恒等于
  • !== 不恒等于

用以比较两个变量所引用的类实例地址是否是一样的。

集合类型的赋值和拷贝行为

字典会被拷贝

将一个字典类型的实例赋值给一个变量或常量时,或者传递给一个函数或方法,这个字典类型会在传递时被拷贝
如果字典的keyvalue是值类型,则会被拷贝;如果是引用类型,则会被拷贝引用。
—— 即, 与key和value的拷贝行为与结构体所存储的属性的拷贝行为相同。

class Name {
    var a :String = "Zhang"
    var b :String = "Wang"
}

var d1 : Dictionary<String, Name > = ["key":Name(), "key2":Name()]
var d2 = d1 // 把d1的值传递给d2,会发生拷贝
println(d2["key2"]!.a) // "Zhang"
println(d1["key2"]!.a) // "Zhang"

d2["key2"]!.a = "Zhang2"
println(d2["key2"]!.a) // 输出 "Zhang2"
println(d1["key2"]!.a) // 输出 "Zhang2",因为Name类是引用类型,所以拷贝的是地址,当修改d2["key2"]的值时候,d1["key2"]也会反映出其修改  

数组的赋值和拷贝

数组的行为要复杂一点咯。
数组类型的实例赋值给一个变量时,数组不会被拷贝,而仅仅传递引用。
但是当数组发生可能改变长度的行为时,数组会被拷贝(注意“可能发生”中的可能)——比如数组的插入、删除、范围赋值等操作。

var a = [1, 2, 3]
var b = a
var c = a

println(a[0]) // 1
println(b[0]) // 1
println(c[0]) // 1 

a[0] = 7 
println(a[0]) // 7
println(b[0]) // 7
println(c[0]) // 7 
// 以为默认为引用传递,所以改变a[0]的值,b[0],c[0]也会同样改变——指向同一地址。

b.append(4)
b[0] = 42
a[0] = 8
println(a[0]) // 8
println(b[0]) // 42
println(c[0]) // 8
// 因为给b数组执行的可能改变长度的操作(append),所以b数组产生了拷贝行为,不再跟a数组、c数组指向同一已地址。 所以 b[0]的修改不会再影响a[0]、c[0]的值;而a、c仍是同一地址,所以修改a[0]同样会影响c[0] 

确保数组的唯一性

.unshare()方法

调用数组的unshare()方法,确保其是唯一的。

var a = [1, 2, 3]
var b = a
var c = a
a.unshare() 
a[0] = 7
println(a[0]) // 7
println(b[0]) // 1
println(c[0]) // 1

a.unshare()被调用之后,a[0]的修改不会再影响b[0]跟c[0]

判断两个数组是否共用相同元素

嗯,不错使用恒等于===不恒等于!==
也可以使用范围判断:

var a = [1, 2, 3]
var b = a
println(a===b) //false 因为a和b指向的地址一样,但a和b自身的地址不一样
println(a[0...2] === b[0...2]) // true 因为b[0...2]跟b[0...2]的地址是一样的(a到b时引用传递)

a.unshare()
println(a===b)
println(a[0...2] === b[0...2]) // false  因为调用unshare()之后,a、b指向的地址不再一样

强制复制数组: copy()方法

注意:
如果你仅需要确保你对数组的引用是唯一引用,请调用unshare方法,而不是copy方法。unshare方法仅会在确有必要时才会创建数组拷贝。copy方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/258

Swift学习笔记(8)—— 枚举

8.枚举

JAVA的枚举语法很简单,用起来也很简单。
Swift的枚举看起来更复杂,当然也更强大一些。


基本语法

与JAVA和C一样,使用enum关键字。
但是不一样的是声明成员的时候还需要case关键字。
每行一个case,N个变量,同一行有多个变量时用逗号,隔开。

enum CC {
    case aa , bb ,cc
}

enum CC2 {
    case aa
    case bb ,cc
}

enum CC3 {
    case aa, bb, cc
}

这三个写法都OK~~

赋值

类型推导在这里依然有用。
并且把变量声明为枚举类型之后,再次赋值时可以省略掉类型名,只用一个点.开始就好。

var cValue : CC = CC.aa
var cValue2 : CC = .aa  // 省略类型名
var cValue3 = CC.aa     // 这里会自动进行类型推导
cValue3 = .bb            // cValue3被推导为CC类型之后,赋值时就可以把CC省略  

实例值(有些翻译为关联值)

可以给枚举的成员设置其他类型的值,设置是每个成员可以有不同类型的的。
——这个就叫做就是实例值。

enum Barcode {//就像这样
    case UPCA(Int, Int, Int)
    case QRCode(String)
}

意思是:

“定义一个名为Barcode的枚举类型,它可以是UPCA的一个实例值(Int,Int,Int),或者QRCode的一个字符串类型(String)实例值。”

然后赋值的时候需要带上实例值了:

var code : Barcode = .UPCA(1,2,3)

用sitch的时候,要使所有的实例值也是完备的。
Also,可以用_匹配任意值。

switch code {
case Barcode.UPCA(_,_,0):
    println("UPCA")
case Barcode.UPCA(_,_,1...5):   
    println("UPCA55")
case Barcode.UPCA(_,_,_):
    println("UPCA2")
case .QRCode:
    print("QRCODE")
}

原始值(Raw Values)

就是默认值咯?不太一样,原始值貌似是不可更改的
声明枚举的时候指定一个类型,然后每个成员都会有这个类型的原始值。

enum Name : String{ //在这里指定原始值的类型
    case A = "Zhang"
    case B  = "Wang"
}
var name = Name.A
println(name.toRaw()) //输出 Zhang

toRaw()/fromRaw(Object raw)

toRaw()获取指定成员的原始值.
fromRaw(Object raw) 获取具有指定原始值的成员:返回一个可选值。

自增

声明为Int的枚举,可以只给第一个成员显式设定原始值,其余的成员原始值会自动递增。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/252

Swift学习笔记(7)—— 闭包

作为一个JAVA程序员,之前对闭包几乎没接触。(嗯,JAVA7开始支持闭包了,BUT So What?)
乍一看这个概念还挺绕。


7.闭包

Swift中闭包的定义,包括如下三种——
1. 全局函数
2. 嵌套函数
3. 闭包表达式
暂且把闭包理解为一个匿名函数吧~?

闭包表达式

基本语法

{ (parameters) -> returnType in
    statements
}
//或者语句较短时可以放到一行
{ (参数表) -> 返回类型 in statements }

//一个例子: sort函数的第二个参数是一个闭包
reversed = sort(names, { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

类型推导

类型推导在Swift中的用处很多,这里又是一个体现。
可以根据上下文推断闭包的参数类型和返回类型。

//上面的闭包可以简写成:
{(s1,s2) in return  s1 > s2}

单表达式隐式返回

表达式只有一行的时候可以省略return

//省略 return
{ (s1,s2) in s1 > s2 }
//调用
sort(naems,{  (s1,s2) in s1 > s2 })

参数名称缩写

可以直接用 $0,$1等…来调用参数。

//使用参数名称缩写
{ ($0 > $1) }
//调用
sort(naems,{ ($0 > $1) })

运算符函数

因为>运算符本身就是一个(String,String)->Int的符号函数。
所以这里可以直接用>作为参数传入到sort函数。

// 简称成 >
sort(names,>)

我只能说:太!凶!残!了!

尾随闭包

当闭包作为一个函数的最后一个参数时,可以使用尾随闭包的形式。

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

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

嗯?好像就是把半个小括号提前了? o(╯□╰)o
这么做的好处是——据说可以让代码更简洁。

当只有一个闭包做参数时,可以把小括号省略掉

捕获值

闭包可以捕获上下文中的中常量或变量,被捕获的常量或变量即使在原来的域中不再存在,在闭包中仍可以使用和修改它。
捕获这个词用的真生动啊!
举个栗子:

//定义一个返回类型为 ()-> Int 类型函数
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

//fun1被赋值为:一个每次调用都会增加3的 ()->Int 函数
var fun1 = makeIncrementor(forIncrement: 3)
fun1()// 值为 3
fun1()// 值为 6

在这个例子中:
incrementor是一个内嵌函数,即一个闭包。
捕获了外部的runningTotal变量,并且即使外部的runningTotal被销毁,闭包内仍能保持和使用它(每次让丫增加3)。

闭包是引用类型

就是指针的意思喽?
即使用一个把一个闭包赋值给一个常量,闭包本身的内容仍然是可以修改的。



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/246

Swift学习笔记(6)—— 函数

函数的定义方法、返回值等都跟C/JAVA有差异。

  • 外部参数名
  • 默认参数
  • 可变参数
  • 函数类型
  • 嵌套函数
    ——都是要注意的点啊,还蛮多的。

6.函数

函数定义的方法

    func sayHello(personName: String) -> String {
        let greeting = "Hello, " + personName + "!"
        return greeting
    }
  • 关键字 func * 函数名 sayHello * 参数表—— (名称: 类型) * 返回类型 -> String

无返回值函数

去除 ->和返回类型即可。(不需要类似JAVA的void关键字)

多重返回值函数:使用元组

func count(string: String) -> (vowels: Int, consonants: Int, others: Int){
    return (1,1,2)
}

外部参数名

定义函数的时候在参数表的名称前多加一名称,则第一个为外部参数名,第二个为内部参数名

func fun1(name arg1: String , age arg2: Int){
    //其中的name跟age是外部参数名
    //arg1跟arg2是内部参数名
    print("\(arg1) is \(arg2) year's old")
}

内部参数名在函数体内部使用,外部参数名那个在调用时使用(且必须使用)。

fun1(name:"Qing",age:4)// 结果输出: Qing is 4 year's old

这么做的优点是增加代码的可读性。

简写外部参数

参数名前加#表示参数使其同时作为内部参数名和外部参数名。

func fun1(#name: String , #age: Int){}

默认参数

参数名后加一个赋值语句可以给其设一个默认参数。

func fun1(#name: String , age: Int = 5){
    println("\(name) is \(age) year's old")
}
fun1(name: "Qing" , age:4) //Qing is 4 year's old
fun1(name: "Bang")          //Bang is 5 year's old ,这里的5调用了默认参数

可变参数

参数类型后带三个点...表示,在函数内部被当做是数组处理。

func fun2(number : Int...) -> Int{
    // 这里会把number当做是一个Int[]数组。
    return 0
}

注意: 一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义

常量参数和变量参数

函数参数默认为常量,是不可更改的。
如果想声明为变量参数,使用var关键字。

func fun3(var s : String){
    //这里s即可以更改了
}

输入输出参数

使用inout关键字定义一个输入输出参数,此类参数的修改可以在函数体外的其他地方被反映。
调用函数的时候参数前要使用&符号。 妈蛋,这不是就是C语言的指针么!

函数类型

所有参数的类型以及返回值的类型,共同组成函数的类型。
(输入)->(输出)
如上述的

  • fun1类型为 (String,Int)->()
  • fun2类型为 (String,Int)->Int
  • fun3类型为 (String)->()

注意Void跟空元组的类型是一样的

函数类型的使用

函数类型可以用于函数类型变量的赋值等(作为一个类型使用)。
可以作为参数类型。
可以作为返回值类型。

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

嵌套函数

定义在其他函数体中的函数……



这笔记只是给我自己看的,用以标记一些我觉得需要记的或者有意思的一些知识点,有很多我觉得没必须要赘述的东西就没记,所以是不系统的,只作提示用。
感谢 numbbbbb以及其他贡献者: https://github.com/numbbbbb/the-swift-programming-language-in-chinese
当然,还有英文页: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70


http://www.barryzhang.com/archives/243