Swift基础学习(二)

上篇《Swift基础学习(一)》:
https://www.jianshu.com/p/33fe93587be0

本篇学习总结:

  • 函数
  • 枚举
  • 类和结构体
  • 属性
  • 类的构造函数
  • 类的析构函数

好了,带着知识点,我们一一开始阅读吧 😊

函数

函数相当于OC中的方法,是一个独立的代码块,用来执行特定的任务。

函数书写格式:

func 函数名(参数一:参数类型,参数二:参数类型,...) -> 返回值类型 {
    代码块
    return 返回值
}
// 如果没有返回值 -> 及Void 可以省略
// 函数的调用
函数名 (参数名:参数一,参数名:参数二);

1.没有参数没有返回值的函数

func about() -> Void { // 可写作 func about() {
    print("Hello Swift")
}
about()

2.有参数没有返回值的函数

func sayHi (saySting : String) -> Void {
    print(saySting)
}
sayHi(saySting: "hello swift")

3.没有参数有返回值的函数

func sayHi () -> String {
    return "hello swift"
}
print(sayHi())

4.有参数有返回值的函数

func sayHi (sayString : String) -> String {
    return sayString + "hello swift"
}
print(sayHi(sayString: "xx_cc "))

没有定义返回类型的函数实际上会返回一个特殊的类型Void,它其实是一个空的元组,租用相当于没有元素的元组,可以写作()

内部参数(形式参数名)和外部参数(实际参数标签)

内部参数也叫形式参数,是指在函数内部能看到的标识符名称,外部参数也叫实际参数,是指在函数外部能看到的标识符名称。默认情况下所有参加同时是内部参数和外部参数,像我们上面的示例中,func 函数名(参数一:参数类型,参数二:参数类型,...) -> 返回值类型,参数名一和参数名二既是内部参数又是外部参数,当我们使用此类函数的时候可以看到其参数名,看一下示例。

// 默认情况
func sum(num1:Int , num2:Int) -> Int {
    return num1 + num2
}
// 调用方法时可以看到 num1,num2两个标识符参数名
// sum(num1: <#T##Int#>, num2: <#T##Int#>)

当不希望显示外部参数时,可以在标识符前添加_用来表示隐藏其外部参数名

func sum(fistNum num1:Int , _ num2:Int) -> Int {
    return num1 + num2
}
// 此时调用函数时,没有num2外部参数名
sum(fistNum: <#T##Int#>, <#T##num2: Int##Int#>)

注意:以上三个示例函数,因为参数名不同因而是三个完全不同的函数,即每一个函数都有一个特定的函数类型,它由形式参数类型,返回类型组成。

可变参数
可变参数即,参数个数可变,可以传零或者多个特定类型的值,传入到可变参数中的值为函数的主题中被当作是对应类型的数组,Swift中可变参数的使用相较于OC中更为简单。

// ...表示参数类型固定,但是参数的个数不确定,
func sum(num : Int...) -> Int {
    var total = 0
// 
    for n in num {
        total += n
    }
    return total
}
// 随意添加多个相同类型参数
sum(num: 20,30,40,50,60,70)

多返回值函数
多返回值函数实际上返回值时一个元组,使用元组实现了函数可以返回多种数据

func sum(num : Int...) -> (min : Int, max : Int) {
    var maxvalue = num[0]
    var minvaleue = num[0]
    for n in num {
        if n > maxvalue {
            maxvalue = n
        }else if n < minvaleue{
            minvaleue = n
        }
    }
    return (minvaleue,maxvalue)
}
let value = sum(num: 20,30,40,50,60,70)
print(value.max)
pring (value.min)

注意:元组在函数的返回类型中又可能没有值,用一个可选元组返回类型来说明整个元组的可能为nil,在可选元组类型的( )后面添加一个?,例如(Int,Int)?来表示这是一个可选元组。类似(Int,Int)?的可选元组类型和包含可选类型的元组( (Int?, Int?))是不同的,对于可选元组类型,整个元组是可选的,而不仅仅是元组里面的单个值为可选类型。

默认参数
可以在命名参数的时候为其附上默认值

func makeCoffee(coffeeName:String = "雀巢") -> String {
    return "制作了一杯\(coffeeName)"
}
makeCoffee(coffeeName: "拿铁") // 制作了一杯拿铁
makeCoffee()  // 制作了一杯雀巢 

注意:在参数较多时,通常把不带有默认值的形式参数放到函数的形式参数列表中带有默认值的形式参数前边,不带有默认值的形式参数通常对函数有重要的意义,把它们写在前边可以便于发现无论是否省略带默认值的形式参数,调用的都是同一个函数。

指针参数

当需要在函数内修改参数值时,可以传递指针参数,用来传递地址,使用inout表示是一个指针参数。

var m : Int = 20
var n : Int = 10

func swapNum(num1: inout Int ,num2: inout Int){
    let temp = num1
    num1 = num2
    num2 = temp   
}
swapNum(num1: &m, num2: &n)
print(m)  // 10
print(n)  // 20

函数类型

每一个函数都有一个特定的函数类型,它由形式参数类型,返回类型组成。

func sum(num1:Int , num2:Int) -> Int {
    return num1 + num2
}

例如:上面的sun函数需要传入两个Int类型的值,返回一个Int类型的值,那么sun函数的类型就是(Int,Int) -> Int,也读作:sun函数是有两个Int类型的形式参数,并且返回一个Int类型值的函数类型

函数类型的使用

既然Swift拥有函数类型,那么我们可以像其他使用类型一样使用函数类型,我们可以给一个常量或者变量定义一个函数类型,并且为变量指定一个相应的函数值。

func sum (num1 : Int ,num2 : Int) -> Int {
    return num1 + num2
}
// 不同的函数如果有相同的匹配类型的话,就可以指定相同的变量,和费函数的类型一样
var mathFunc : (Int , Int) -> Int = sum
mathFunc(3, 5)

// 和其他类型一样,当指一个函数为常量或者变量的时候,可以使用Swift类型推断
let mathFunc2 = mathFunc

函数类型作为形式参数类型

我们可以将函数类型作为其他函数的形式参数类型,也就是将函数作为其他函数的参数,这样可以极大的扩展我们的函数方法,将方法的部分实现交给调用者在调用函数时去实现

func sum (num1 : Int ,num2 : Int) -> Int {
    return num1 + num2
}
// 这个函数需要我们传入一个有两个Int类型参数并且返回值是Int类型的函数类型 和两个Int类型的函数
func sumWithMath (math : (Int ,Int) -> Int , num1 : Int ,num2 :Int) -> Void {
    let sumValue = math (num1, num2)
    print(sumValue)  // print 10
}
sumWithMath(math: sum(num1:num2:), num1: 5, num2: 5)

函数类型作为返回类型

既然函数类型可以像使用其他类型一样,那么函数类型也一定可以作为返回类型使用,即在函数的返回箭头 ->后面直接跟上一个完整的函数类型即可,直接来看下面的例子

// 首先定义了两个对参数执行加减操作的函数
func sum (num1 : Int ,num2 : Int) -> Int {
    return num1 + num2
}
func minus (num1 : Int ,num2 : Int) -> Int {
    return num1 - num2
}
// 定义函数,传入一个Bool值,返回加函数还是减函数
func SelectMathWith (add : Bool) -> (Int,Int)->Int {
    if add {
        return sum(num1:num2:)
    }else{
        return minus(num1:num2:)
    }
}
// 拿到方法返回的函数并进行调用
let sumMath = SelectMathWith(add: true)
sumMath(5, 5)

内嵌函数

Swift中可以在函数的内部定义另外一个函数,这就是内嵌函数,内嵌函数在默认情况下在外部是被隐藏起来的,但是仍然可以通过包裹他们的函数来调用他们,包裹的函数也可以返回它内部的一个内嵌函数来在另外的范围里使用,对于上面的例子,可以重写将sum与minus函数作为内嵌函数,在函数内部定义

func SelectMathWith (add : Bool) -> (Int,Int)->Int {   
// 函数内部声明内嵌函数,并将其返回供外部使用
    func sum (num1 : Int ,num2 : Int) -> Int {       
        return num1 + num2
    }   
    func minus (num1 : Int ,num2 : Int) -> Int {       
        return num1 - num2
    }   
    if add {
        return sum(num1:num2:)
    }else{
        return minus(num1:num2:)
    }
}
let sumMath = SelectMathWith(add: true)
sumMath(5, 5)

枚举

Swift中的枚举语法跟OC中的一样,使用enum关键词来定义一个枚举,然后将其所有的你定义内容放在一个打括号中。

enum Derection {
    case up
    case down
    case left
    case right
}
// 多个成员值也可以出现在同一行中,用逗号隔开即可
enum Derection {
     case up,down,left,right
}

需要注意的是在swift中,uptown,left,right并不一定代表0,1,2,3,如果在定义枚举的时候没有为其内容附具体的值,那么枚举此时的值就是每个枚举相对应的字符串。

我们可以通过点语法来获取的枚举的值

// let n1 : Derection = .up 
var n1 = Derection.up
print(n1) // print up
// 当我们为n1赋值时,n1的类型会被推断出来,因此我们在为n1赋一个新的值时,就可以省略类型,直接赋值。
n1 = .down

在定义枚举时枚举成员可以使用相同类型的默认值,我们可以使用rewValue来访问枚举成员的默认值。

enum MethodType : String{
    case get = "get"
    case post = "post"
    case put = "put"
    case delete = "delete"
}
// 而当我们存储的是Int类型时,同OC一样只需要给第一个枚举成员分配默认值即可
enum Type : Int {
    case get = 0, post, put, delete // 仅 Int 可以
}

let type = MethodType.post.rawValue
// 或者通过下面这种方式获取,创建出的值位MethodType可选类型,因为可能为nil
let type1 = MethodType(rawValue: "put")
let str = type3?.rawValue

类和结构体

类和结构体对比

相同点

  • 定义属性用来存储值
  • 定义方法用于提供功能
  • 定义下标脚本用来允许使用下标语法访问值
  • 定义初始化起用来初始华状态
  • 可以被扩张来默人所没有的功能
  • 遵循写以来针对特定的类型提供标准功能

类有而结构体没有的额外功能

  • 允许一个类继承另一个类的特征
  • 类型转换允许你在运行检查和解释一个类实例的类型
  • 反初始化器允许一个类实例释放任何其被分配的资源
  • 引用计数允许不只对一个类实例的引用(结构体在代码中通过复制来传递,并不会使用引用计数)

定义结构体

结构体是由一系列具有相同类型或不同类型的数据结构构成的数据集合,结构体指的是一种数据结构,当我们定义一个结构体,相当于定义一个全新的swift类型,结构体是值类型,在方法中传递时是值传递。

定义结构体语法

// 定义结构体
struct location {
    // 属性和方法
    var x : Double
    var y : Double
//    var z : Double
    //方法
    func test() -> Void {
        print("这是结构体之中的test方法")
    }
    // 改变成员属性 : 如果在函数中修改了成员属性那么函数前必须加上 mutating
    mutating func moveH(distance : Double) -> Void {
        self.x += distance
    }
    
    // 给结构体扩充构造函数
    // 1. 默认情况下系统会为每一个结构体提供一个默认的构造函数,并且该构造函数要求给每一个成员属性赋值
    // 2. 构造函数都是以init开头的,并且构造函数不需要返回值。
    // 3. 在构造函数结束时,必须保证所有成员属性有被初始化
    init(_ x : Double, _ y : Double) {
        self.x = x
        self.y = y
    }
}

// 结构体使用
//var center = location(x: 20, y: 30) // 系统默认的初始化器
var center = location(20, 30)
center.x // 通过点语法访问结构体属性
center.x = 30 // 也可以通过点语法访问结构体属性并赋新值
center.test() // 结构体 test方法
center.moveH(distance: 15) // 结构体moveH方法
print(center)

结构体和枚举都是值类型
值类型:当它被指定到常量或者变量,或者被传递给函数时会拷贝的类型,也就是说我们重新声明一个变量并且为其附值之后,此时新的变量与之前的附值的变量是两个完全不同的实例,我们修改新的变量中的值,不会影响之前的变量。

struct direction {
    var left : Int
    var right : Int
}

let direction1 = direction(left: 10, right: 20)
var direction2 = direction1
// 此时direction2中的值与direction1中相同,但是他们是两个完全不同的实例。修改direciton2中的值不会影响direction1中的值。
direction2.left = 30

注意:Swift的String,Array和Dictionary类型是作为结构体来实现的,这意味着字符串,数组和字典在它们被附值到一个新的常量或者变量,亦或者它们本身被传递到一个函数或者方法中的时候,其实传递了拷贝。

这种行为不同于基础库中的NSString,NSArray和NSDictionary,它们是作为类来实现的,而不是结构体。NSString,NSArray和NSDictionary实例总是作为一个已存在实例的引用而不是拷贝来附值和传递。

定义类
类的使用
swift也是一门面向对象开发的语言,面向对象的基础是类,类产生了对象,当我们定义类时可以没有父类,如果没有父类那么该类就是rootClass,但是通常情况下,我们在定义类时继承自NSObject(非OC中的NSObject)

定义类的语法

class 类名称 : 继承父类 {

}
class Person : NSObject {
    // 一般属性用var 变量 或者可选类型
    // 规范:如果属性是值类型,则初始化空值
    // 如果属性是对象类型,则初始化为nil值
    var name : String = ""
    var age : Int = 0
    var view : UIView?

}

// 创建类的对象
let p = Person() // 小括号调用其实是在调用构造函数

类是引用类型
相对于值类型,在引用类型被附值到一个常量,变量或者本身被传递到一个函数的时候它是不会被拷贝的,而是通过引用之乡同一个实例。实际上,他们只是先通实例的不同命名。

class Person : NSObject {
    var name : String = ""
    var age : Int = 0
}

let person1 = Person()
person1.name = "bb"
person1.age = 18

let person2 = person1
person2.name = "aa"
// 修改person2的name属性,person1的name属性也会被修改
person1.name // "aa"

swift中可以使用===相同于 和 !==不同于 来判断两个常量或者变量是否引用自同一个类实例。

属性

swift中属性大致分为三种,存储属性,计算属性,类型属性。存储属性会存储常量或者变量作为实例的一部分,反之计算属性会计算值,类型属性则与类型本身相关联。

存储属性
存储属性作为特殊类和结构体实例一部分的向量与变量,我们可以为存储属性提供一个默认值作为它定义的一部分,也可以在初始化的过程中设置和修改存储属性的初始值。

class Person : NSObject {
    var name : String = ""
   let age : Int = 0
}
let person1 = Person()
person1.name = "xx_cc"
person1.age = 2 // 我们可以修改name属性不能修改age属性

计算属性
类和结构体同能定义计算属性,计算属性实际并不存储值,而是提供一个读取器和一个可选的设置器来简洁得到和设置其他的属性的值。
举个例子

class Student {
    // 类的属性定义
    // 存储属性
    var name : String = ""
    var age : Int = 0
    var mathScore : Double = 0.0
    var chinaScore : Double = 0.0
    
    // 计算属性
    // 例子:需求:获取某学生的平均成绩
    var averageScore : Double { // set get方法可以省略,当省略set方法时,此属性也就是只读属性
        return (self.chinaScore + self.mathScore) * 0.5
    }
}
let stu = Student() // 调用系统默认提供的构造函数
stu.mathScore = 99.7
stu.chinaScore = 98
stu.averageScore // 调用计算属性 OC中很多没有参数的方法在Swift中可以写成计算属性

类型属性
swift中类型属性相当于OC中静态常量变量,当对于特定类型的所有实例都通过的值时,可以使用类型属性来定义属性,类型属性只有一个拷贝,无论我们创建多少类对应的实例,因此类型属性子初始化时必须有一个默认值。

// 类属性 使用static标识并且需要有一个默认值
static var courseCount : Int = 0

监听属性的变化
swift中属性观察者会坚定属性的变化,每当一个属性的值被设置,属性官场着都会被调用,即使这个值与该属性的当前值相同。我们可以为定义的任意存储属性添加属性观察者。

class Person {
    var name : String = "" {
        // 属性监听器 :监听属性的改变 一般二选其一 
        // 1. 监听属性即将发生改变 其实还没有改变
        willSet{ //(newName) 可以通过小括号中加变量名修改 newValue的名字
            // 如果在即将改变时想要拿到新的值
            print("属性即将改变")
            print(name) // 当前值 未发生改变
            print(newValue) // 将要赋的值
        }
        // 2. 监听属性已经发生改变
        didSet{
            print(name) // 当前值,已经发生改变新值
            // 旧的值
            print("属性发生改变")
            print(oldValue)
        }
    }
}
let person = Person()
person.name = "aa"
person.name = "bb"
  • willSet会在该值被存储之前被调用。
  • didSet会在一个新值被存储后被调用。
    如果你实现了一个willSet观察者,新的属性值会以常量形式参数传递。你可以在你的willSet实现宗纬这个参数定义名字。如果你没有为它命名,那么它会使用默认的名字newValue
    同样,如果你实现了一个didSet观察者,一个包含就属性值的常量形式参数将会被传递,你可以为它命名,也可以使用默认的形式参数名oldValue。如果你在属性自己的didSet观察者里给自己赋值,你赋值的新值就会取代刚刚设置的值。

注意:父类属性的willSet和didSet观察者会在子类初始化器中设置时被调用,它们不会在类的父类初始化器调用中设置其自身属性时被调用。

类的构造函数

构造函数类似于OC中的初始化方法,init方法,默认情况下创建一个类时,必然会调用一个构造函数,即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数,编译器也会提供一个默认的构造函数,而如果是继承自NSObject,可以对父类的构造函数进行重写。

举个例子

class Person {
    var name : String = ""
    var age : Int = 0

    // 注意:如果有自定义构造函数,那么会将系统提供的构造函数覆盖掉,如果我们不希望系统提供的构造函数 需要明确自己实现
    init() {
        
    }
    
    init(name : String , age : Int) {
        self.name = name
        self.age = age
    }
  
    init(dict : Dictionary <String ,Any>) {
        /*
         let dictName = dict["name"]
         name = dictName as! String // 强制转化为string ,使用起来非常危险
        */
        if let name = dict["name"] as? String {
            self.name = name
        }
        if let age = dict["age"] as? Int {
            self.age = age
        }
    }
}

let p = Person() // 系统默认提供的
let p1 = Person(name: "cl", age: 18) // 使用自己提供的构造函数
let p2 = Person(dict: ["name":"cl","age":18])
p2.name

我们可以使用KVC来为类中的属性赋值,但是使用KVC是有条件的

  • 1.继承自NSObject
  • 2.必须在构造函数中,先调用Super.init()
  • 3.调用setValuesForKeys(dict)
  • 4.如果字典中某一个key没有对应的属性,则需要重写setValueforUnderfinedKey方法
class Person : NSObject {
    var name : String = ""
    var age : Int = 18
    // kvc方式赋值
    init(dict : [String : Any]) {
        super.init()
        setValuesForKeys(dict)
    }
 
    override func setValue(_ value: Any?, forUndefinedKey key: String) {
    }
}
let p = Person(dict: ["name":"cl","age":18])

类的析构函数

Swift中析构函数也就是OC中的delloc函数,当对象销毁时系统会自动调用函数,同时Swift会自动释放不再需要的实例以释放资源。

  • 1.swift通过自动引用计数(ARC)处理实例的内存管理
  • 2.当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
  • 3.通常在析构函数中释放一些资源(如移除通知等等)
//deinit {
//    // 析构函数 -> 释放过程
//}

class Person {
    var name : String = ""
    var age : Int = 18
    // 重写析构函数来监听对象的销毁
    deinit {
        print("Person对象 -deinit")
    }
}
var p : Person? = Person()
p = nil  // print Person对象 -deinit

本篇先记录这些,如有错误,不吝赐教!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,069评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,212评论 1 287
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,912评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,424评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,741评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,194评论 1 206
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,553评论 2 307
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,289评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,923评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,251评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,775评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,144评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,698评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,936评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,658评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,214评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,159评论 2 258

推荐阅读更多精彩内容