当前位置: 首页 > 工具软件 > Flying-Swift > 使用案例 >

Swift3.0从入门到放弃(三)

罗均
2023-12-01

Swift当中的函数

关于Swift中的函数的叫法来自于官方文档function(函数)和method(方法),和OC中的方法C中的函数基本差不多,大体可以分为无参无返、有参无返、无参有返和有参有返四中类型。

Swift中函数的简单定义

// Swift中的函数定义格式为:
// func 函数名(参数列表) -> 返回值类型 {

//    代码块
//    return 返回值
// }


// 无参无返回
func eat() ->Void { // 当没有返回值时 -> void 可以省略

    print("正在吃饭");
}

// 有参无返
func eatWith(food : String) {

    print("正在吃\(food)");
}

// 无参有返
func receiveMessage() -> String {

    return "10086"
}

// 有参有返
func getSumWith(num1 : Int, num2 : Int) ->Int {

    return num1 + num2
}

// 函数调用
eat()
eatWith(food: "油条")
print(receiveMessage())
print(getSumWith(num1: 10, num2: 20))

Swift中函数参数的使用

import UIKit


// 外部和内部参数
// 外部参数:可以在函数外部看见参数名称的参数叫做外部参数
// 内部参数:只能在函数内部看见参数名称的参数叫做内部参数
// 在Swift3.0中,函数中的参数默认既是外部参数又是内部参数
func calculate(num1 : Int, num2 : Int) -> Int
{
    return num1 + num2
}

func calculate1(tmp1 num1 : Int, tmp2 num2 : Int) -> Int
{
    return num1 + num2
}

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

calculate(num1: 10, num2: 20) // 此时num1、num2既是外部参数也是内部参数
calculate1(tmp1: 20, tmp2: 10) // 此时tmp1、tmp2是外部变量,相当于num1和num2起的别名,num1和num2为内部变量
calculate2(30, 20) // 在函数参数名前面加上下划线,可将函数参数变为内部变量



// 可变参数
// 在参数类型后面加上 ... 可将参数变为可变参数,参数的个数由一个到多个不等,此时的nums是数组类型
func calculateWithNums(_ nums : Int...) ->Int {

    var sum = 0
    for num in nums {

        sum += num
    }
    return sum
}

calculateWithNums(20,10,11,32)




// 默认参数
func getCoffeeWithName(_ name : String = "雀巢") -> String {

    return "获得一杯\(name)"
}

getCoffeeWithName() // 获得一杯雀巢 如果不传参数 就是用参数默认值




// 指针参数
// 函数的参数传入的是内存地址 在参数的类型前面加上 inout 关键词
func exchangeNums(_ num1 : inout Int, _ num2 : inout Int ) {

    let tmp = num1
    num1 = num2
    num2 = tmp
}

var num1 = 10; var num2 = 20
print("num1 = \(num1) num2 = \(num2)") // num1 = 10 num2 = 20
exchangeNums(&num1, &num2)
print("num1 = \(num1) num2 = \(num2)") // num1 = 20 num1 = 10

Swift中的枚举

枚举是定义了一个通用类型的一组相关的值,可以在代码中以安全的方式使用这些值。
在C和OC当中枚举是指定相关名称为整型值(有名字的常量)。
在Swift中枚举更加的灵活,不必给每一个枚举成员提供一个值,也可以提供值为字符串,字符,或是整型和浮点型。

Swift中如何定义一个枚举

import UIKit

// 定义一个枚举
enum GenderType {

    case male

    case female

    case unknown

}

// 如何使用枚举的值
let gender_01 : GenderType = .male // 根据上下文能够推导出枚举类型,可以直接写.真实类型
let gender_02 : GenderType = GenderType.female // 也可以写 枚举类型.真实类型


// 如何给枚举成员赋值
enum DirectionType : String { // 赋值前 必须在枚举类型后面标注枚举成员赋值的类型

    case east = "east"
    case south = "south"
    case west = "west"
    case north = "north"

}

let dir = DirectionType(rawValue: "south") // DirectionType?
if let dir = DirectionType(rawValue: "west") { // 由于返回值可能为nil 所以是可选类型
    print(dir.rawValue)
}

// 枚举类型的定义方式二
enum CoffeeType : Int {

    case nescafe = 0, cappuccino, mocha
}

print(CoffeeType.cappuccino.rawValue) // 枚举成员值的类型若为Int型,赋值一个,后面的自动自增

Swift中的结构体

结构体(struct)是由一系列相同类型或不同类型的数据构成的数据集合,结构体是值类型,在方法和函数中是值传递。

Swift中结构体的基本使用

import UIKit

// 1.定义一个结构体
struct HWLocation {

    // 属性
    var x : Double

    var y : Double

}


// 2.结构体的创建
let struct_01 = HWLocation(x: 10, y: 20)


// 3.给结构体扩充方法
struct Location {

    // 属性
    var x : Double
    var y : Double

    // 扩充方法一
    // 打印坐标方法
    func printLocation() {
        print("Location x : \(self.x) y : \(self.y)")
    }

    // 扩充方法二
    // 更改坐标方法 (扩充函数中涉及修改属性值时 函数前必须添加 mutating 关键字) 
    // mutating 变化
    mutating func changeLocation(_ x : Double, _ y : Double) {

        self.x = x
        self.y = y
    }
}

var struct_02 = Location.init(x: 20, y: 100)
struct_02.printLocation() // 结构体扩充方法调用
struct_02.changeLocation(9, 9) // 结构体扩充方法 修改属性的值 (PS: 结构体必须由var可变修饰才能更改)
print(struct_02) // Location(x: 9.0, y: 9.0) 修改成功


// 4.给结构体扩充构造函数 (系统默认会给结构体扩充全属性赋值构造函数)
// 注意 : 构造函数必须以init开头;构造函数不需要写返回值;在构造函数中必须对每一个属性进行初始化
struct Registration {

    var userName : String
    var userAge : Int
    var ID_card : String
    var email : String

    // 扩充构造方法
    init(_ userName : String = "用户名", _ userAge : Int = 18, _ ID_card : String = "666", _ email : String = "123.qq.com") {

        self.userName = userName
        self.userAge = userAge
        self.ID_card = ID_card
        self.email = email
    }
}

let register = Registration("香格里拉", 20)
print(register)

Swift中的类

Swift也是一门面向对象的语言,若想实例化对象必须要用到类,在Swift当中我们用关键词class来定义一个类。

Swift中类的定义

import UIKit

// 定义一个简单的Person类,Prson类可以不继承任何类,主要看是否需要用到父类的属性和方法。
// 如果用不到父类的属性和方法,那就可以不继承,使得当前类更轻量级。
// 系统会自动给类扩展一个构造方法,但是并不会自动初始化类中的属性。
// 所以对于类中的属性 1.可以定义时进行初始化、2.定义成可选类型、3.在init构造方法中进行手动初始化。
// 个人经验 : 如果属性是对象类型(UIView、NSUrl等)写成可选类型;如果是值类型(Int、 String等)直接定义时进行初始化。
class Person : NSObject {

    var name : String
    var age : Int

    // 重写构造方法 在方法前面加上 override
    override init() {

        name = "陈冠希"
        age = 18
    }

}

// 创建Person对象
let p = Person()
print("姓名:\(p.name) 年龄:\(p.age)")

Swift中多种属性

在Swift中类的属性大体分为三种:存储属性、计算属性、类属性。
import UIKit


class Person : NSObject {

    // 存储属性 : 用来存储实例的常量和变量
    var name : String = "张柏芝"
    var age : Int = 0
    var mathScore : Double = 59.9
    var englishScore : Double = 70.0

    // 计算属性 不同点是赋值的是一个大括号 大括号内可以有计算属性的get和set方法 
    // 一般不会用到set方法 若只有返回值(get方法or只读属性) 可以省略set和get方法 直接return
    // 计算属性完整写法
    var averageScore : Double {

        set {

        }

        get {

            return (mathScore + englishScore) * 0.5
        }

    }

    // 计算属性省略写法
    var printInfo : String {

        return "姓名:\(name) 年龄:\(age) 数学成绩:\(mathScore) 英语成绩:\(englishScore)"
    }


    // 类属性 是和整个类相关的属性 并且使用类名进行访问
    static var courseCount : Int = 2

}


// 创建人类的对象
let p = Person()

// 访问存储属性
print(p.name)

// 访问计算属性
print(p.averageScore)

// 访问类属性
print(Person.courseCount)

Swift中的属性监听器

import UIKit

// 在Swift中对类中属性的监听操作可以用属性监听器 在OC当中一般利用属性的get方法进行监听
class Person : NSObject {

    var name : String = "" {

        // 属性即将发生改变
//        willSet {
//            print("属性即将发生改变 旧值:\(name) 新值:\(newValue)")
//        }
        willSet(newName) { // 在即将发生变化监听器中 可将系统提供的 newValue 起别名
            print("属性即将发生改变 旧值:\(name) 新值:\(newName)")
        }

        // 属性发生改变
//        didSet {
//            print("属性发生改变 旧值:\(oldValue) 新值:\(name)")
//        }
        didSet(oldName) { // 在发生变化监听器中 可将系统提供的 oldValue 起别名
            print("属性发生改变 旧值:\(oldName) 新值:\(name)")
        }

    }


}


// 创建人类对象
let p = Person()
// 属性发生变化 触发属性监听器
p.name = "小布什"
p.name = "奥巴马"

Swift中的构造函数

import UIKit

// 定义人类
class Person : NSObject {

    var name : String = ""
    var age : Int = 0
    var height : Double = 0.0


    // 被覆盖的自带构造函数 可手动再扩展一个
//    override init() {
//        
//    }

    // 自定义构造函数(一旦自定义构造函数,编辑器自动给类扩展的构造函数将会被覆盖)
    // 构造函数中,要用到属性时,可以省略self. 但是若出现命名冲突时,必须加上self.
    init(name : String, age : Int, height : Double) {

        self.name = name
        self.age = age
        self.height = height

    }

    // 复杂版的字典转模型构造方法 (但是灵活性不好)
//    init(dict : [String : Any]) {
//        
//        if let tmpName = dict["name"] as? String {
//            self.name = tmpName
//        } else {
//            self.name = ""
//        }
//        
//        if let tmpAge = dict["age"] as? Int {
//            self.age = tmpAge
//        } else {
//            self.age = 0
//        }
//        
//        if let tmpHeight = dict["height"] as? Double {
//            self.height = tmpHeight
//        } else {
//            self.height = 0.0
//        }
//        
//    }

    // 利用KVC方式字典转模型赋值 (KVC属于NSObject的方法,所以必须继承自NSObject才能使用)
    init(dict : [String : Any]) {

        super.init() // 在调用KVC方法前 调用super的init方法
        setValuesForKeys(dict)
    }

    // 避免KVC找不到对应的key值报错 空实现系统以下方法
    override func setValue(_ value: Any?, forUndefinedKey key: String) {}

}


// 创建人类对象
//let p = Person(name: "陈冠希", age: 18, height: 1.88)
//print(p.name, p.age, p.height)

// 通过字典给Person对象赋值
let dict : [String : Any] = ["name" : "谢霆锋", "age" : 19, "height" : 1.88]
let p2 = Person(dict: dict)
print(p2.name, p2.age, p2.height)

Swift中的析构函数

Swift中的析构函数其实就是OC中的dealloc方法,在ARC下不可手动调用。Swift通过自动引用计数来处理实例对象的内存管理,当一个实例对象的引用计数为0时,系统会自动调用该对象的析构函数。通常在西沟函数中,会释放一些资源,如监听通知的对象等。
mport UIKit

class Person : NSObject {

    deinit {

        print("人被释放了")

    }

}

var p : Person? = Person()
p = nil // 当一个实例对象被释放时,系统自动调用该对象的析构函数

Swift中的循环引用问题

在Swift中系统也是通过实例对象的引用计数来管理内存的,当一个对象被强引用时,引用计数+1,当强引用消失时,引用计数-1;在实例对象的引用计数为0时,对象被释放。但是在ARC下也有可能发生内存泄漏问题,比如出现循环引用。
import UIKit

// 创建Person类
class Person : NSObject {

    var name : String = ""
//    var book : Book?
    weak var book : Book? // 改为若引用

    deinit {
        print("人已经被销毁")
    }
}

// 创建Book类
class Book : NSObject {

    var name : String = ""
    var owner : Person?

    deinit {
        print("书已经被销毁")
    }
}

// 创建书对象 & 人对象
var book : Book? = Book()
var p : Person? = Person()

p?.book = book
book?.owner = p

// 当我们把实例对象的强应用去掉后 发现被没有调用两个对象的析构函数 证明此时两个对象都未被释放
// 解决方案一 : 在其中一个对象中将引用另一个对象的属性前面添加 weak 变为若引用
// 解决方案二 : 使用 unowned 关键词修饰也可以 但是不安全 基本不用
p = nil
book = nil

Swift中的可选链

import UIKit

// 可选链 : 多个可选类型组成的链条叫做可选链
// ?.就是可选链,系统会对可选链中的每个环节进行自动判断,当可选类型有值就进行解包,
// 当可选类型为空时就赋值为nil,所以对于整个可选链语句的返回值不是nil就是一个可选类型。
// 运用可选连当其中的某个环节为nil时,整个可选链条就会失效
class Person : NSObject {

    var name : String = ""
    var dog : Dog?

}

class Dog : NSObject {

    var name : String = ""
    var toy : Toy?

}

class Toy : NSObject {

    var name : String = ""
    var price : Double = 0


    func flying() {
        print("飞盘在非")
    }

}

// 实例化对象
let person = Person()
let dog = Dog()
let toy = Toy()

dog.toy = toy
person.dog = dog


// 1.取值

// 1.1危险方式(强制解包)
print(person.dog!.toy!.price)

// 1.2复杂层层可选绑定方式
if let d = person.dog {
    if let t = d.toy {
        print(t.price)
    }
}

// 1.3可选链方式取值
if let price = person.dog?.toy?.price {
    print(price);
}

// 2.通过可选链赋值 (强制解包和层层绑定方式大体如上)
person.dog?.toy?.price = 9.99
if let price = person.dog?.toy?.price {
    print(price);
}

// 3.通过可选链调用函数 (强制解包和层层绑定方式大体如上)
person.dog?.toy?.flying()

Swift中协议的基本使用

import UIKit

// 1. 定义一个协议
protocol TestProtocol {

    // 协议中提供的函数声明
    func eat()
    func sleep()
}

// 1.1 如果一个类不继承自任何其他类 那么在类名后面加上 : 协议名称 来遵守协议
class person : TestProtocol {

    // 实现协议中的方法
    func eat() {
        print("吃饭")
    }

    func sleep() {
        print("睡觉")
    }
}

// 1.2 如果一个类有父类 就在继承的父类后面加上 ,协议名称 来遵守协议
class Student : NSObject, TestProtocol {

    // 实现协议中的方法
    func eat() {
        print("吃饭")
    }

    func sleep() {
        print("睡觉")
    }

}


// 2. 协议在代理模式中的使用 协议后面加上 :class 规定此协议只能被类遵守(避免被枚举、结构体等遵守与类中代理属性weak修饰词冲突)
protocol DriverProtocol : class {

    func drivering()

}

class Boss : NSObject {

    // 定义代理属性 用weak修饰 防止循环引用
    weak var driver : DriverProtocol?

    func goHome() {

        driver?.drivering() // 让遵守协议的人开车送老板回家
    }

}

class Driver : NSObject, DriverProtocol {

    func drivering() {
        print("开车")
    }
}

let boss = Boss()
let driver = Driver()
boss.driver = driver
boss.goHome() // 开车

// 3. 将协议中的方法设置成 optional 类型 
// 可以使用 optional 关键字修饰协议中可选择实现的方法,但是optional是OC中的特性,所以必须在协议的前面
// 加上@objc(Swift2.0)、在方法的 optional 关键字前面也加上 @objc(Swift3.0)
@objc protocol Test2Protocol : class {

    func RequiredFunc() // 必须实现的方法

    @objc optional func optionalFunc() // 可选择实现的电方法
}

class Teacher : NSObject, Test2Protocol {

    func RequiredFunc() {
        print("必须实现的方法")
    }

}
 类似资料: