美文网首页
Swift笔记

Swift笔记

作者: Zoros | 来源:发表于2017-04-19 15:17 被阅读22次

    一. 常量&变量的使用注意

    1.注意一:
    在开发中,优先使用常量(let). 只有发现标识符需要修改时,再使用变量.选择let更加安全
    目的: 防止在其它不希望修改的地方,不小心将值修改掉

    2.注意二:
    常量的本质: 指向的内存地址不可以修改,但是可以通过内存地址找到对应的对象,之后修改对象内部的属性

    OC中创建对象:

    UIView *view = [[UIView alloc] init];
    

    Swift中创建对象:

    类型()
    var view : UIView = UIView()
    view = UIView()
    

    1> 指向的内存地址不可以修改

    let view : UIView = UIView() // 0x100
    view = UIView() // 0x200
    

    2> 但是可以通过内存地址,找到对应的对象,之后修改对象内部的属性

    view.alpha = 0.5
    view.backgroundColor = UIColor.blue
    

    二. 创建对象补充

    UIButton = UIButton()
    btn.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    btn.backgroundColor = UIColor.orange
    btn.setTitle("按钮", for: .normal)
    view.addSubview(btn)
    

    三、 类型推导

    类型推导
    1> 如果在定义一个标识符时,有直接给该标识符进行赋值. 那么可以将标识符后面的类型省略掉
    2> 编译器会根据我们后面赋值的类型,自动推导出前面标识符的类型, 这个过程就叫做类型推导
    3> 可以通过 option + 鼠标左键 来查看标识符的类型

    四、基本运算

    不同类型之间不能直接计算,(OC可以,因为有隐式转换)

    let m = 20
    let n = 10.5
    

    错误写法:

    let result = m + n
    

    正确写法:
    将Int类型转成Double: Double(标识符)
    将Double类型转成Int : Int(标识符)

    let result1 = Double(m) + n
    let result2 = m + Int(n)
    

    注:类型转换实质就是调用构造函数转换类型

    五、逻辑分支(if的使用)

    OC的语法

        int a = 20
        if (a > 0) { }
        if (a) { }
    

    Swift中if和OC的区别
    1> if后面的()可以省略掉,执行分支必须有{}
    2> 没有非0)即真的概念,只有Bool(true/false)

    let a = 10
    if a > 0 {
        print("a大于0")
    }
    
    if a != 0 {
        print("a不等于0")
    }
    

    else if的使用

    let score = 88
    if score < 0 || score > 100 {
        print("不合理的分数")
    } else if score < 60 {
        print("不及格")
    } else if score < 80 {
        print("及格")
    } else if score < 90 {
        print("良好")
    } else {
        print("优秀")
    }
    

    六、逻辑分支(guard的使用)

    可以理解为给判断条件取反,与 if!() 类似

    let age = 20
    func online(age : Int) {
        if age >= 18 {
            if 带了身份证 {
                if 带了钱 {
                    print("可以上网")
                } else {
                    print("回家拿钱")
                }
            } else {
                print("回家拿身份证")
            }
        } else {
            print("回家去")
        }
    }
    
    func online(age : Int) {
        // guard : 守卫
        // 1.判断年龄是否大于18岁
        guard age >= 18 else {
            print("回家去")
            return
        }
        
        // 2.判断是否带了身份证
        guard 带了身份证 else {
            print("回家拿身份证")
            return
        }
        
        // 3.判断是否带了钱
        guard 带了钱 else {
            print("回家拿钱")
            return
        }
        print("留下上网")
    }
    online(age: age)
    

    七、逻辑分支(switch的使用)

    1.switch基本使用
    1> switch后面的()可以省略
    2> case语句结束时,可以不加break,默认不穿透,如果需要穿透需要添加fallthrouth关键字
    3> 一次性可以判断多个条件
    4> 可以判断任意类型的对象

    let sex = 1
    // sex 0 : 男  1 : 女
    switch sex {
    case 0:
        print("男")
    case 1:
        print("女")
    default:
        print("其它")
    }
    

    2.基本用法补充
    1> 在swift中,switch后面case可以判断多个条件
    2> 如果希望case结束时,产生case穿透.case结束时,加上fallthrough

    switch sex {
    case 0, 1:
        print("正常人")
        fallthrough
    default:
        print("非正常人")
    }
    

    3.switch判断其它类型
    3.1.判断浮点型

    let m = 3.14
    switch m {
    case 3.14:
        print("和π相等")
    default:
        print("和π不相等")
    }
    

    3.2.判断字符串

    let a = 20
    let b = 30
    let oprationStr = "*"
    var result = 0
    
    switch oprationStr {
    case "+":
        result = a + b
    case "-":
        result = a - b
    case "*":
        result = a * b
    case "/":
        result = a / b
    default:
        print("不合理的操作符")
    }
    

    3.3.判断区间类型

    let score = 88
    switch score {
    case 0..<60:
        print("不及格")
    case 60..<80:
        print("及格")
    case 80..<90:
        print("良好")
    case 90...100:
        print("优秀")
    default:
        print("不合理的分数")
    }
    

    八、循环使用(for循环)

    OC语法

    for (int i = 0; i < 10; i++) {
          NSLog(@"%d", i);
    }
    

    Swift语法

    for i in 0..<10 {
        print(i)
    }
    
    for i in 0...10 {
        print(i)
    }
    

    在swift开发中,如果一个变量/常量暂暂时不会使用,那么可以使用 _ 通配符 来代替

    for _ in 0..<10 {
        print("hello world")
    }
    

    九、循环使用(while&dowhile循环)

    OC语法

    int a = 0
    while (a < 10) {
        a++
    }
    

    swift中while循环和OC区别
    1> while后面()可以省略
    2> 没有非0(nil)即真 Bool(true/false)

    var m = 0
    while m < 10 {
        m += 1
        print(m)
    }
    
    repeat {
        m -= 1
        print(m)
    } while m > 0
    

    十、字符串的使用

    String字符串类型,通过结构体实现的,更加轻量级,支持快速遍历,NSString中的并不支持,String可以和NSString无缝的转换
    NSString继承NSObiect是一个对象,不支持遍历

    1.定义字符串
    1> 定义不可变字符串 : 使用let修饰

    let str : String = "hello swift"
    let str = "Hello Swift"
    

    2> 定义可变字符串 : 使用var修饰

    var strM = "Hello World"
    strM = "Hello China"
    

    2.获取字符串的长度

    let length = str.characters.count
    

    3.字符串的拼接
    3.1.字符串之间的拼接

    let str1 = "我很帅"
    let str2 = ",真的很帅!"
    // OC拼接方式 [NSString stringwithFormat:@"%@%@", str1, str2];
    let str3 = str1 + str2
    

    3.2.字符串和其它标识符之间的拼接

    let name = "NIE"
    let age = 19
    let height = 1.87
    let infoStr = "my nams is \(name), age is \(age), height is \(height)"
    

    3.3.字符串拼接过程中的格式化: 03:04

    let min = 3
    let second = 50
    let timeStr = String(format: "%02d:%02d", min, second)
    

    4.字符串的截取

    let urlString = "www.baidu.com"
    

    4.1.方式一:(建议使用此方式)
    将String类型转成NSString类型,再进行截取: as NSString

    let header1 = (urlString as NSString).substring(to: 3)
    let range1 = NSMakeRange(4, 5)
    let middle1 = (urlString as NSString).substring(with: range1)
    let footer1 = (urlString as NSString).substring(from: 10)
    

    4.2.方式二:
    直接使用String类型方法,进行截取

    let headerIndex = urlString.index(urlString.startIndex, offsetBy: 3)
    let header2 = urlString.substring(to: headerIndex)
    
    let startIndex = urlString.index(urlString.startIndex, offsetBy: 4)
    let endIndex = urlString.index(urlString.startIndex, offsetBy: 9)
    let range = Range(startIndex..<endIndex)
    let middle2 = urlString.substring(with: range)
    
    let footerIndex = urlString.index(urlString.endIndex, offsetBy: -3)
    let footer2 = urlString.substring(from: footerIndex)
    

    十一、数组的使用

    1.数组的定义
    数组的类型:
    1> Array<String>
    2> [String] (推荐)

    let array = ["MM", "NN", "HH"]
    
    var arrayM = Array<String>()
    var arrayM = [String]()
    

    2.对可变数组的基本操作
    增:

    arrayM.append("nienie")
    arrayM.insert("杰克", at: 0)
    

    删:

    arrayM.remove(at: 0)
    arrayM.removeFirst()
    arrayM.removeLast(2)
    

    改:

    arrayM[0] = "LL"
    

    查:

    let item = arrayM[0]
    

    3.对数组的遍历
    3.1.获取数组的长度

    let count = array.count
    

    3.2.对数组进行遍历(可以获取到下标值)

    for i in 0..<count {
        print(array[i])
    }
    

    3.3.对数组进行遍历(不需要获取下标值)

    for item in array {
        print(item)
    }
    

    3.4.对数组进行遍历(既获取下标值,又获取元素)

    for (index, item) in array.enumerated() {
        print(index, item)
    }
    

    3.5倒序遍历

    for name in array.reversed() {
        print(name)
    }
    

    4.数组的合并
    如果两个数组中存放的是相同的元素,那么在swift中可以对两个数组进行相加,直接合并

    let array1 = ["AA", "BB"]
    let array2 = ["CC", "DD"]
    let array3 = [12, 20, 30]
    
    let resultArray = array1 + array2
    let result = array1 + array3 错误写法
    

    十二、字典的使用

    1.如何定义字典
    编译器会根据[]中是一个个元素(数组),还是键值对(字典)

    let dict = ["name" : "AA", "age" : 18, "height" : 1.88] as [String : Any]
    let dict = ["123" : "321", "abc" : "cba"] 不需要进行转化
    Array<String> --> [String]
    let dict : Dictionary<String, Any> = ["name" : "AA", "age" : 18, "height" : 1.88]
    let dict : [String : Any] = ["name" : "AA", "age" : 18, "height" : 1.88]
    
    泛型字典类型:
    [String : String]
    

    2> 定义可变字典 : 使用var修饰

    var arrayM = [String]()
    var dictM = Dictionary<String, Any>()
    var dictM = [String : Any]()
    

    2.对可变字典的基本操作(增删改查)

    增:
    dictM["name"] = "AA"
    dictM["age"] = 18
    dictM["height"] = 1.88
    
    删:
    dictM.removeValue(forKey: "height")
    
    改:
    dictM["name"] = "BB"
    dictM.updateValue("BB", forKey: "name")
    
    查:
    dictM["age"]
    

    3.遍历字典
    3.1.遍历字典中所有的key

    for key in dict.keys {
        print(key)
    }
    

    3.2.遍历字典中所有的value

    for value in dict.values {
        print(value)
    }
    

    3.3.遍历字典中所有的key/value

    for (key, value) in dict {
        print(key, value)
    }
    

    4.字典合并

    var dict1 : [String : Any] = ["name" : "AA", "age" : 18]
    let dict2 : [String : Any] = ["height" : 1.88, "phoneNum" : "+86 110"]
    let resultDict = dict1 + dict2
    或:
    for (key, value) in dict2 {
        dict1[key] = value
    }
    

    十三、元组的使用

    1.使用数组保存信息

    let infoArray : [Any] = ["why", 18, 1.88]
    let arrayName = infoArray[0] as! String
    print(arrayName.characters.count)
    

    2.使用字典保存信息

    let infoDict : [String : Any] = ["name" : "why", "age" : 18, "height" : 1.88]
    let dictName = infoDict["name"] as! String
    print(dictName.characters.count)
    

    3.使用元组保存信息(取出数据时,更加方便)
    3.1.写法一:

    let infoTuple = ("AA", 18, 1.88)
    let tupleName = infoTuple.0
    let tupleAge = infoTuple.1
    print(tupleName.characters.count)
    

    3.2.写法二:

    let infoTuple1 = (name : "AA",age : 18, height : 1.88)
    infoTuple1.age
    

    3.3.写法三:

    let (name, age, height) = ("AA", 18, 1.88)
    

    十四、可选类型的使用

    一个对象都会有两面性,即有值或为nil
    在开发中,只有可选类型才能赋值为nil, 其它类型都不能赋值为nil
    在类型的后面添加'?'声明可选类型 例:

    var a : Int?
    

    常量的可选类型是没有意义的,因为只有一次赋值机会
    必选类型,一定要有值,并且一定不能为nil
    可选类型不能够直接参与计算

    '?'表示可选类型
    在打印的时候会自动带上Optional字样
    
    '!'
    可选类型不能够直接计算,如果参与计算系统会自动提示添加'!'强制解包
    将'可选类型'的值,解包
    报错fatal(致命的) error: unexpectedly found nil while unwrapping() an Optional value
    '!'存在风险,如果为nil,程序就会boom
    提示程序员在这个地方可能存在风险需要考虑是否需要判断为nil
    
    '??'
    空合并运算符得到的结果是必选类型
    运算符的优先级比较低,在使用的时候需要使用()来提高优先级
    相当于三目运算符
    快速判断对象是够为nil,如果为nil,就去??后面设置的默认值
    带有红圈白点的错误提示,一般都是系统能够自动修改的错误
    

    1.定义可变类型: 泛型集合
    1> 定义方式一:

    var name : Optional<String> = nil
    

    2> 定义方式二: 语法糖

    var name : String? = nil
    

    2.给可选类型赋值
    2.1.赋值方式一:

    name = Optional("AA")
    

    2.2.赋值方式二:

    name = "AA"
    

    3.取出可选类型中的值
    从可选类型中取值: 可选类型 + ! --> 强制解包

    print(name!)
    

    4.注意: 强制解包非常危险, 如果可选类型为nil,那么强制解包就会崩溃

    if name != nil {
        print(name!)
    }
    

    5.可选绑定(固定格式) : 该语法用于可选类型, 使我们使用起来可选类型更加方便
    1> 判断name是否有值, 如果没有值,则直接不执行{}
    2> 如果name有值,那么系统会自动对可选类型进行解包, 并且将解包后的结果赋值给前面的tempName

    if let tempName = name {
        print(tempName)
    }
    
    if let name = name {
        print(name)
    }
    

    十五、可选类型的应用

    只要一个类型有可能为nil, 那么这个标识符的类型一定是一个可选类型
    1.将字符串转成Int类型

    let m : Double = 2.44
    let n = Int(m)
    
    let str : String = "123"
    let num : Int? = Int(str) // 123/nil
    

    2.根据文件名称:123.plist, 获取该文件的路径

    let path : String? = Bundle.main.path(forResource: "123.plist", ofType: nil) // string/nil
    

    3.将字符串转成NSURL
    如果字符串中有中文,那么就是转化不成功, 返回结果 nil

    let url : NSURL? = NSURL(string: "http://www.520it.com") // URL/nil
    let url = URL(string: "http://www.520it.com")
    

    4.从字典中取出元素

    let dict : [String : Any] = ["name" : "why", "age" : 18]
    let value = dict["neme"] // Any/nil
    

    十六、类型转化

    1.之前的as使用

    let str = "www.xunjian.com"
    (str as NSString).substring(to: 11)
    

    2.as? as! --> 将Any类型转成具体类型

    let dict : [String : Any] = ["name" : "AA", "age" : 18, "height" : 1.88]
    

    2.1. as? 的用法
    通过as?转成可选类型
    as? : 转成的类型是一个可选类型, 系统会自动判断tempName是否可以转成String, 如果可以转成, 那么获取字符串, 如果转化不成功, 则返回nil

    let tempName = dict["name"]
    let name = tempName as? String
    if let name = name { // 可选绑定
        print(name)
    }
    
    if let name = dict["name"] as? String {
        print(name)
    }
    

    2.2. as! 的用法
    通过as!转成具体类型
    as! : 将类型转成具体的类型
    注意: 如果转化不成功, 则程序会直接崩溃
    建议: 如果确定转化成功,再用as!, 平时不建议

    let tempName1 = dict["name"]
    let name1 = tempName1 as! String
    

    十七、函数的使用

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

    func about() -> Void {
        print("iPhone8")
    }
    
    func about1() {
        print("iPhone8")
    }
    about()
    

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

    func callPhone(phoneNum : String) {
        print("打电话给:\(phoneNum)")
    }
    callPhone(phoneNum: "+86 110")
    

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

    func readMsg() -> String {
        return "吃饭了吗?"
    }
    let msg = readMsg()
    print(msg)
    

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

    func sum(num1 : Int, num2 : Int) -> Int {
        return num1 + num2
    }
    let result = sum(num1: 20, num2: 30)
    print(result)
    

    十八、函数的其他用法

    1.内部参数&外部参数
    内部参数: 在函数内部能看到标识符名称, 该标识符就是内部参数
    外部参数: 在函数外部能看到标识符名称, 该标识符就是外部参数
    默认情况下,所有的参数都是内部参数,也是外部参数
    修改外部参数: 在标识符前加上外部参数名称即可
    如果不希望显示外部参数, 可以在标识符前加上 _

    func sum(_ num1 : Int, _ num2 : Int) -> Int {
        return num1 + num2
    }
    
    let result = sum(num1: 20, num2: 30)
    let result = sum(abc: 20, num2: 30)
    let result = sum(20, 30)
    

    2.可变参数

    func sum(_ nums : Int...) -> Int {
        var total = 0
        for n in nums {
            total += n
        }
        return total
    }
    

    3.默认参数

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

    4.指针参数

    var m : Int = 20
    var n : Int = 30
    
    func swapNum(num1 : inout Int, num2 : inout Int) {
        let temp = num1
        num1 = num2
        num2 = temp
    }
    swapNum(num1: &m, num2: &n)
    print("m:\(m) n:\(n)")
    

    十九、枚举类型

    1.枚举类型的定义

    enum MethodType : String {
        case get = "get"
        case post = "post"
        case put = "put"
        case delete = "delete"
    }
    

    2.创建枚举具体的值

    let type1 : MethodType = .get
    let type2 = MethodType.post
    let type3 = MethodType(rawValue: "put") // 值/nil
    let str = type3?.rawValue
    

    3.给枚举类型进行赋值

    enum Direction : Int {
        case east = 0
        case west = 1
        case north = 2
        case south = 3
    }
    let d1 : Direction = .east
    let d2 = Direction(rawValue: 1)
    

    4.枚举类型定义方式二:

    enum Type : Int {
        case get = 0, post, put, delete
    }
    

    二十、结构体的使用

    1.定义结构体

    struct Location {
        // 属性
        var x : Double
        var y : Double
        var z : Double
        
        // 方法
        // 最普通的函数: 该函数是没有用到成员属性
        func test() {
            print("结构体中的test函数")
        }
        
        // 改变成员属性 : 如果在函数中修改了成员属性, 那么该函数前必须加上mutating
        mutating func moveH(_ distance : Double) {
            self.x += distance
        }
    

    给结构体扩充构造函数
    1> 默认情况下, 系统会为每一个结构体提供一个默认的构造函数, 并且该构造函数, 要求给每一个成员属性进行赋值
    2> 构造函数都是以init开头, 并且构造函数不需要返回值
    3> 在构造函数结束时, 必须保证所有的成员属性有被初始化

    init(x : Double, y : Double) {
          self.x = x
          self.y = y
    }
       
    init(xyStr : String) {
            // 20,30 --> ["20", "30"]
            let array = xyStr.components(separatedBy: ",")
            let item1 = array[0]
            let item2 = array[1]
            
            /*
            if let x = Double(item1) {
                self.x = x
            } else {
                self.x = 0
            }
            
            if let y = Double(item2) {
                self.y = y
            } else {
                self.y = 0
            }
            */
            
            // ?? 判断前面的可选类型是否有值
            // 有值, 则解包, 没有值,则使用后面的值
            self.x = Double(item1) ?? 0
            self.y = Double(item2) ?? 0
        }
    }
    

    2.创建结构体对应的值

    var center = Location(x: 20, y: 30)
    let ceter = Location(x: 20, y: 30, z: 40)
    
    3.系统结构体的创建方式
    let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
    let size = CGSize(width: 20, height: 20)
    let point = CGPoint(x: 0, y: 0)
    let range = NSRange(location: 0, length: 3)
    

    4.给结构体扩充方法

    center.test()
    //center.moveH(distance: 20)
    center.moveH(20)
    print(center)
    

    5.给结构体扩充构造函数

    Location(x: 20, y: 30)
    Location(xyStr : "20,30")
    

    二十一、类的使用

    1.如何定义类
    OC类的定义:

    @interface Person : NSObject
    @end
    
    @impelment
    @end
    

    Swift类的定义:

    class Person {
        // 类的属性定义
        // 存储属性: 用于存储实例的常量&变量
        var name : String = ""
        var age : Int = 0
        var mathScore : Double = 0.0
        var chineseScore : Double = 0.0
        
        // 计算属性: 通过某种方式计算得来结果的属性,就是计算属性 --> 只读属性
        var averageScore : Double {
            return (chineseScore + mathScore) * 0.5
        }
        
        // 类属性: 和整个类相关的属性, 并且是通过类名进行访问
        static var courseCount : Int = 0
        
        // 给类扩充函数
        // 在OC中写的很多没有参数的方法, 在swift中可以写成计算属性
        func getAverageScore() -> Double {
            return (chineseScore + mathScore) * 0.5
        }
    
    }
    

    二十二、监听类的属性改变

    class Person {
        var name : String = "" {
            // 属性监听器: 选中其中之一即可
            // 监听属性即将发生改变: 还没有改变
            willSet(newName) {
                print(newName)
                print(name)
            }
            
            // 监听属性已经发生改变: 已经改变
            didSet(oldName) {
                print(oldName)
                print(name)
            }
        }
    }
    let p = Person()
    p.name = "AA"
    

    二十三、类的构造函数

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

    类的属性必须有值,如果不是在定义时初始化值,可以在构造函数中赋值

    OC的方法:

     @interface Person : NSObject
    
     @property (nonautomic, copy) NSString *name;
     @property (nonautomic, assign) NSInteger age;
     - (instanceType)initWithName:(NSString *)name age:(NSInteger)age;
     - (instanceType)initWithDict:(NSDictionary *)dict;
    
     @end
     
     Person *p = [Person alloc] init];
     Person *p = [Person alloc] initWithName:@"why" age:18];
    

    Swift的方法:
    方法一:重写了NSObject(父类)的构造方法
    类的属性必须有值,如果不是在定义时初始化值,可以在构造函数中赋值

    class Person: NSObject {
        var name : String
        var age : Int
    
        // 重写了NSObject(父类)的构造方法
        override init() {
            name = ""
            age = 0
        }
    }
    // 创建一个Person对象
    let p = Person()
    

    方法二: 初始化时给属性赋值
    很多时候,我们在创建一个对象时就会给属性赋值,可以自定义构造函数
    注意:如果自定义了构造函数,会覆盖init()方法.即不再有默认的构造函数

    class Person: NSObject {
        var name : String
        var age : Int
        // 在Swift开发中, 如果在对象函数中, 用到成员属性, 那么self.可以省略
        // 注意: 如果在函数中, 有和成员属性重名的局部变量,那么self.不能省略
        // 注意: 如果有自定义构造函数, 那么会将系统提供的构造函数覆盖掉
        // 自定义构造函数,会覆盖init()函数
        init(name : String, age : Int) {
            self.name = name
            self.age = age
        }
    }
    // 创建一个Person对象
    let p = Person(name: "who", age: 18)
    

    方法三:字典转模型(初始化时传入字典)
    真实创建对象时,更多的是将字典转成模型
    注意:
    去字典中取出的是NSObject,任意类型.
    可以通过 as! 转成需要的类型,再赋值(不可以直接赋值)

    class Person: NSObject {
        var name : String
        var age : Int
    
        // 自定义构造函数,会覆盖init()函数
        init(dict : [String : NSObject]) {
            name = dict["name"] as! String
            age = dict["age"] as! Int
        }
    }
    
    // 创建一个Person对象
    let dict = ["name" : "who", "age" : 18]
    let p = Person(dict: dict)
    

    方法四:字典转模型(利用KVC转化)
    利用KVC字典转模型会更加方便
    注意:
    KVC并不能保证会给所有的属性赋值
    因此属性需要有默认值
    基本数据类型默认值设置为0
    对象或者结构体类型定义为可选类型即可(可选类型没有赋值前为nil)
    使用KVC条件
    1> 必须继承自NSObject
    2> 必须在构造函数中,先调用super.init()
    3> 调用setValuesForKeys
    4> 如果字典中某一个key没有对应的属性, 则需要重写setValue forUndefinedKey方法

    class Person: NSObject {
        // 结构体或者类的类型,必须是可选类型.因为不能保证一定会赋值
        var name : String?
    
        // 基本数据类型不能是可选类型,否则KVC无法转化
        var age : Int = 0
    
        // 自定义构造函数,会覆盖init()函数
        init(dict : [String : NSObject]) {
            // 必须先初始化对象
            super.init()
    
            // 调用对象的KVC方法字典转模型
            setValuesForKeysWithDictionary(dict)
        }
    }
    
    // 创建一个Person对象
    let dict = ["name" : "who", "age" : 18]
    let p = Person(dict: dict)
    

    三十四、类的析构函数

    Swift 会自动释放不再需要的实例以释放资源,通过自动引用计数(ARC)处理实例的内存管理,当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
    通常在析构函数中释放一些资源(如移除通知等操作)

    析构函数的写法

    deinit {
        // 执行析构过程
    }
    
    class Person {
        var name : String
        var age : Int
    
        init(name : String, age : Int) {
            self.name = name
            self.age = age
        }
        // 重写析构函数, 监听对象的销毁
        deinit {
            print("Person-deinit")
        }
    }
    
    var p : Person? = Person(name: "cj", age: 18)
    p = nil
    

    三十五、循环引用

    // 1.创建类
    class Person {
        var name : String = ""
        var book : Book?
        
        deinit {
            print("Person -- deinit")
        }
    }
    
    class Book {
        var price : Double = 0
        /*
         OC中表示弱引用
            __weak/__unsafe_unretained(野指针错误)
         Swift中表示弱引用
            weak/unowned(野指针错误)
        */
        // weak var owner : Person?
        // unowned 不能用于修饰可选类型
        unowned var owner : Person = Person()
        
        deinit {
            print("Book -- deinit")
        }
    }
    
    
    // 2.创建两个对象
    var person : Person? = Person()
    person!.name = "AA"
    var book : Book? = Book()
    book!.price = 60.0
    
    person!.book = book
    book!.owner = person!
    
    person = nil
    book = nil
    

    三十六、可选链的使用场景

    class Person {
        var dog : Dog?
    }
    
    class Dog {
        var toy : Toy?
    }
    
    class Toy {
        var price : Double = 0
        
        func flying() {
            print("飞盘在飞ing")
        }
    }
    
    let p = Person()
    let d = Dog()
    let t = Toy()
    t.price = 100
    
    p.dog = d
    d.toy = t
    

    可选链的使用
    ?.代表可选链: 系统会自动判断该可选类型是否有值;如果有值,则解包;如果没有值, 则赋值为nil
    注意: 可选链条获取的值,一定是一个可选类型

    if let price = p.dog?.toy?.price { // Double/nil
        print(price)
    }
    let price = p.dog?.toy?.price
    

    如果可选链中有一个可选类型是没有值, 那么语句直接不执行

    p.dog?.toy?.price = 50
    

    可选链调用方法

    p.dog?.toy?.flying()
    

    三十七、协议的使用

    协议的定义

    protocol SportProtocol {
        // 默认情况下协议中的方法都是必须实现的方法
        func playBasketball()
        func playFootball()
    }
    

    2.定义类,并且遵守协议

    class Teacher : SportProtocol {
        func playFootball() {
            print("踢足球")
        }
        
        func playBasketball() {
            print("打篮球")
        }
    }
    
    
    class Student : NSObject, SportProtocol {
        func playFootball() {
            print("踢足球")
        }
        
        func playBasketball() {
            print("打篮球")
        }
    }
    

    3.协议在代理设计模型中的使用
    定义协议时, 协议后面最好跟上 : class
    delegate的属性最好用weak, 防止产生循环引用

    protocol BuyTicketDelegate : class {
        func buyTicket()
    }
    
    class Person {
        // 定义代理属性
        weak var delegate : BuyTicketDelegate?
        
        func goToBeijing() {
            delegate?.buyTicket()
        }
    }
    

    4.协议方法可选
    optional属于OC特性, 如果协议中有可选的方法, 那么必须在protocol前面加上@objc, 也需要在optional前面加上@objc

    @objc protocol TestProtocol {
        @objc optional func test()
    }
    
    class Dog : TestProtocol {
        func test() {
            
        }
    }
    

    相关文章

      网友评论

          本文标题:Swift笔记

          本文链接:https://www.haomeiwen.com/subject/pifhzttx.html