swift关键字之声明式关键字

声明式关键字

  • associatedtype(关联类型
    • swiftprotocol不能使用<T>这种泛型,但是提供了associatedtype关键字来支持泛型。   

    • 由于协议是定义一些规范(属性、功能方法),然后由类、结构体或者枚举遵循并实现这些规范,所以在面对不同的遵循对象时,协议方法的参数,我们可能会传入不同的类型。如果仅仅因为参数不同,我们就要定义很多的协议方法,对应不同类,那就显得很麻烦。associatedtype就是为了解决这一问题的。

    • 关联类型为协议中的某个类型(任意类型)提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 associatedtype 关键字来指定关联类型,当然你也可以用来设计api用来构建统一的处理结构。

      import UIKit
      
      ///定义协议
      protocol TableViewCell {
          associatedtype T  ///定义时我们并不知道,他的具体类型.
          func updateCell(_ data:T)
      }
      
      struct Model {
          let age:Int
      }
      
      ///遵循TableViewCell协议
      class MyTableViewCell:UITableViewCell,TableViewCell {
          typealias T = Model  ///此时确定协议中定义的未知类型T为 Model
          func updateCell(_ data: Model) {
              
          }
      }
  • typealias(类型别名)
    • typealias 是用来为已经存在的类型重新定义名字的,通过命名,可以使代码变得更加清晰。

    • 它的用法很广泛,简单介绍以下几种typealias应用的场景,来达到熟悉它的目的。

      • 重新定义闭包类型

        swift的闭包书写虽然好看了不少,但是如果大批量的书写闭包还是很累的而且还影响可读性和美观, 所以不妨试试typealias

        typealias DownSuccess = (_ json:String?,_ filePath:String?) -> () ///给比较长的闭包,起一个比较短的别名

        这样我们在使用它的时候只需要:

        func Post(url:String?, paramater:NSDictionary, success:DownSuccess) {
            
        }
      • protocol组合

        protocol在swift中强大了不少,多种不同的protocol可以组合成一个然后用typealias重新命名

        import UIKit
        
        ///协议
        protocol changeName {
            func changeNameTo(name:String)
        }
        
        protocol changeSex {
            func changeSexTo(sex:String)
        }
        
        ///给两个协议起一个别名
        typealias changeProtocol = changeName & changeSex
        
        ///遵守协议
        struct Person:changeProtocol {
            func changeNameTo(name: String) {
                
            }
            
            func changeSexTo(sex: String) {
                
            }
        }
      • 基本类型

        这种用法在swift api中应用很广泛

        public typealias AnyClass = AnyObject.Type
        public typealias NSInteger = Int
      • 自定义类型

        在实际项目过程中,如果有OC和swift混编的情况,不免以后会对OC进行swift化,而OC和swift的命名系统相差很大,所以在重构之后不免要对整个项目进行 搜索-查找-替换 这是项非常耗时耗力的工作,而利用
        typealias 可以巧妙的规避这个问题

        // OC中项目里有个类
        #import "OCClass.h"
        
        // swift重构之后
        impot SwfitClass
        
        typealias OCClass = SwfitClass
  • class:通用、灵活的结构体,是程序的基础组成部分。我们可以使用class关键词声明一个类或者类方法
    • class放在类前面表示声明一个类
      ///class 关键词
      class Person {
          var name:String
          var age :Int
          var gender:String
          
          init(name:String,age:Int,gender:String) {
              self.name = name;
              self.age = age;
              self.gender = gender
          }
      }
    • class放在方法前 类似静态方法  只能用类名来调用
      • class修饰方法时作用和static修饰方法类似都可以用于指定类方法.

        1,static修饰父类类方法 ,则这个方法能被子类继承但是子类不能重写这个方法,class修饰父类类方法,则这个方法能被子类继承也能被子类重写。
        2,如果父类的类方法是使用final class修饰的话,就相当于static, 子类不能重写这个类方法。static自带final class的性质。
        注意:用static关键字指定的方法是静态方法,他是不能被子类重写的

        ///class 关键词
        class Person {
            var name:String
            var age :Int
            var gender:String
            
            init(name:String,age:Int,gender:String) {
                self.name = name
                self.age = age
                self.gender = gender
            }
            
            /// class 修饰的类方法
            class func work(){
                print("带class Type Methods:")
            }
            
            /// 普通方法
            func work(){
                print("Type Methods")
            }
            
            /// static 静态方法
            static func work1(){
                print("带static Type Methods")
            }
        }
        
        class Chinese : Person {
            ///class修饰的类方法能被子类重新
            override class func work() {
                
            }
            ///普通父类实例方法被重新
            override func work() {
                
            }
            ///static修饰的静态方法无法被子类重写
            #warning("无法重写")
            
        }
      • class 修饰的方法和普通方法的区别
        class修饰方法的好处 Person.work()  和 p1.work()  都分别调用了Person类中的work()方法 两者虽然看上去相同  没有什么太大区别 但是在内存分配上会有相当大的区别。使用Person.work() 就不用在堆区开辟一份空间来存放person对象 而 let p1 =Person() 相当于person的alloc init 方法他将在堆区开辟一块空间来存放 p1 对象 。所以在有一定需求下 使用class 修饰方法  要比普通方法 节省内存,运行速度快。
        ///class 关键词
        class Person {
            var name:String
            var age :Int
            var gender:String
            
            init(name:String,age:Int,gender:String) {
                self.name = name
                self.age = age
                self.gender = gender
            }
            
            /// 带class修饰的类方法
            class func work(){
                print("带class Type Methods:")
            }
            
            /// 普通方法
            func work(){
                print("Type Methods")
            }
            
            ///静态方法
            // static func work(){
            //  print("带static Type Methods")
            // }
        }
    • class放在计算属性前面(注意:class只能修饰计算属性,不能修饰存储属性) 

      ///class 关键词
      class Person {
          var name:String
          var age :Int
          var gender:String
          
          ///class 修饰计算属性
          class var instruct:String {
              set(value){
               
              }
              get{
                  return ""
              }
          }
          
          init(name:String,age:Int,gender:String) {
              self.name = name
              self.age = age
              self.gender = gender
          }
          
          /// class 类方法
          class func work(){
              print("带class Type Methods:")
          }
          
          /// 普通方法
          func work(){
              print("Type Methods")
          }
          
          /// static 静态方法
          static func work1(){
              print("带static Type Methods")
          }
      }
  • static(可以自行验证)     
    Swift中表示 “类型范围作用域” 这一概念有两个不同的关键字,它们分别是static和class。static 关键字声明静态变量或者函数,它保证对应的作用域当中只要一份,同时也不需要依赖实例化。

    注意:用static关键字指定的方法是静态方法,他是不能被子类重写的。

    • 适用场景

      修饰存储属性,修饰计算属性,修饰类型方法

      ///static 关键词
      class Person {
          var name:String
          var age :Int
          
          ///static 修饰的存储属性 (static修饰的存储属性,无法通过init赋值,所以必须要定义时就要初始化)
          static var gender:String = ""
          
          ///static 修饰计算属性
          static var instruct:String {
              set(value){
               
              }
              get{
                  return ""
              }
          }
          
          init(name:String,age:Int) {
              self.name = name
              self.age = age
          }
          
          /// static 修饰的静态方法
          static func work1(){
              print("带static Type Methods")
          }
      }
    • class和static 关键字区别

      • class 和 static 相同点

        1.可以修饰方法,static 修饰的方法叫做静态方法,class修饰的叫做类方法

        2.都可以修饰计算属性

      • class 和 static 不同点

        1. class 不能修饰存储属性

        2. class 修饰的计算属性可以被重写,static 修饰的不能被重写

        3. static 可以修饰存储属性,static修饰的存储属性称为静态变量(常量)

        4. static 修饰的静态方法不能被重写,class 修饰的类方法可以被重写

        5. class 修饰的类方法被重写时,可以使用static 让方法变为静态方法

        6. class 修饰的计算属性被重写时,可以使用static 让其变为静态属性,但它的子类就不能被重写了

        7. class 只能在类中使用,但是static 可以在类,结构体,或者枚举中使用

    • 示例如下
      class Person{
          ///static修饰的静态存储属性
          static var describe:String = " 我是Swift"
          
          
          ///class修饰的计算属性
          class var score:Int{
              return 0
          }
          
          ///static修饰的计算属性
          static var number:Int{
              return 100
          }
          
          /// class 修饰的类方法可以被子类重写,static 修饰的静态方法不能被重写
          class func  getScore()->Int{
              return score
          }
          
          static func aaa() -> Int {
              return 10
          }
      }
      
      class Man: Person {
      
          // 重写计算属性 可以使用static 来重写哦,但是static 重写后,就不能被它的子类再次重写了
          static override var score:Int{
              return 1
          }
          
          /// 不能重写static修饰的计算性属性
          //    static override var number:Int{
          //        return 101
          //    }
          
          // 重写类方法时可以使用static 让其变成静态方法
          static override func  getScore()->Int{
              return score
          }
      
          /// 不能重写static修饰的方法
          // static override func aaa() -> Int {
          //     return 10
          // }
      }
  • deinit:当一个类的实例即将被销毁时,会调用这个方法。deinit属于析构函数,当对象结束声明周期时,系统会自动执行析构函数。相当于OC中的dealloc方法
    class Person {
        var name:String
        var age:Int
        var gender:String
        
        ///构造函数
        init(name:String,age:Int,gender:String) {
            self.name = name
            self.age = age
            self.gender = gender
        }
        
        ///析构函数
        deinit {
            ///从堆中释放,并释放相关资源
        }
    }
    • 析构函数的作用
      我们通常在deinit函数中会进行一些资源释放和通知移除的工作等
      • 对象销毁
      • KVO移除
      • 通知移除
      • NSTimer销毁  
  • enum

    定义了包含一组有关联的值的类型,并可以以一种类型安全的方式使用这些值。在 Swift 中,枚举是一等类型,拥有在其它语言中只有 class 才会支持的特性。具体使用详情请参考相关API

    enum Gender {
        case male
        case female
    }
  • extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。Extension 与 Object-C的category有点类似,但是extension比起category来说更加强大和灵活。它不仅可以扩展某种类型或者结构体的方法,同时它还可以与protocol等结合使用,编写出更加强大和灵活代码,它可以为特定的class,strut,enum或者protocol添加新的特性。当你没有权限对源代码进行改造的时候,此时可以通过extension来对类型进行扩展。extension有点类似于OC的类别--Category,但稍微不同的是category有名字,而extension没有名字。

    注意:

    扩展可以添加新的计算属性,但不能添加存储属性,也不能向已有属性添加属性观察。

    • extension对swift开发经常用到的方法(包括但不限于)

      • 定义示例方法和类型方法

        class Person {
            ///存储属性
            var name:String;
            var age:Int
            var gender:String;
            
            ///构造函数
            init(name:String, age:Int, gender:String) {
                self.name = name;
                self.age  = age;
                self.gender = gender;
            }
        }
        
        extension Person {
            //MARK: - 定义示例方法
            func sayHello(){
                print("Hello,world!")
            }
            
            //MARK: - 定义类方法
            class func sayHello(){
                print("Hello,world1")
            }
        }
        
        var person = Person(name: "Swift", age: 7, gender: "girl")
        person.sayHello()
        Person.sayHello()
      • 添加计算属性和计算静态属性

        • 计算属性

          //MARK: - 添加计算属性
          extension Double {
              var km:Double{
                  return self * 1_000.0
              }
              
              var m:Double{
                  return self
              }
              
              var cm:Double{
                  return self/100.0
              }
          }
        • 静态计算属性

          //MARK: - 添加计算属性字符串
          extension String {
              static var descr:String {
                  return "这是一个string类型字符串"
              }
          }
      • 定义下标

        class Person {
            ///存储属性
            var name:String;
            var age:Int
            var gender:String;
            
            ///构造函数
            init(name:String, age:Int, gender:String) {
                self.name = name;
                self.age  = age;
                self.gender = gender;
            }
        }
        
        extension Person {
            //MARK: - 定义下标
            subscript( key:String) -> String{
                if key == "name" {
                    return self.name
                } else if key == "age" {
                    return "\(self.age)"
                } else if key == "gender" {
                    return "\(self.gender)"
                } else {
                    return "未知属性"
                }
            }
        }
        
        var person = Person(name: "Swift", age: 7, gender: "girl")
        var value:String = person["name"];
        print(value)
      • 提供新的构造器

        struct Size {
          var width = 0.0
          var height = 0.0
        }
         
        struct Point {
          var x = 0.0
          var y = 0.0
        }
         
        struct Rect {
          var origin = Point()
          var size = Size()
        }
         
        // 这里因为Rect结构体都提供了属性的默认值,它可以自动会有一个默认的构造器和一个成员逐一的构造器,
        // 即:init()方法和init(origin:Point, size: Size)
        // 那么我们可以直接使用:
        let defaultRect = Rect() // 调用默认构造器
        let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
         
        // 下面我们给Rect扩展一个构造器
        extension Rect {
          init(center: Point, size: Size) {
             let originX = center.x - size.width / 2.0
             let originY = center.y - size.height / 2.0
             
             // 调用本类自动得到的成员逐一构造器
             self.init(origin: Point(x: originX, y: originY), size: size)
          }
        }  
    • fileprivate:访问控制权限,只允许在定义源文件中访问。

      Swift 中由低至高提供了 privatefileprivateinternalpublicopen 五种访问控制的权限。默认的 internal 在绝大部分时候是适用的,另外由于它是 Swift 中的默认的控制级,因此它也是最为方便的。
        1.  private 表示代码只能在当前作用域或者同一文件中同一类型的作用域中被使用
        2.  fileprivate 表示代码可以在当前文件中被访问,而不做类型限定。   

    • privatefileprivate 修饰属性

      • fileprivate 修饰的属性,能在当前文件内访问到,不管是否在本类的作用域;  (注释:一个文件内可以定义很多类)

      • private 只能在本类的作用域且在当前文件内能访问. 

        //ViewController.swift
        class Person: NSObject {
            fileprivate var name:String = "man"
            private var age:Int = 1
        }
        
        class ViewController: UIViewController {
            override func viewDidLoad() {
                super.viewDidLoad()
            
                let p = Person()
                print(p.name)
                
                //编译器不会提示age属性 如果强行写p.age
                //会报错'age' is inaccessible due to 'private' protection level
                //print(p.age)
            }
        }
        log:  man  
    • private 和 fileprivate 修饰方法时
      //ViewController.swift
      class Person: NSObject {
          fileprivate var name:String = "man"
          private var age:Int = 1
          
          fileprivate func printNameAndAge(){
              print("name:\(name) age:\(age)")
          }
          
          private func setNameAndAge(name:String,age:Int){
              //xxx
          }
      }
      override func viewDidLoad() {
              super.viewDidLoad()
              let p = Person()
              p.printNameAndAge()
              
              //用private修饰的方法不能够被调用到
              //'setNameAndAge' is inaccessible due to 'private' protection level
              // p.setNameAndAge("superMan",100)
          }
    • 子类是否能使用到private和fileprivate修饰的代码

      如果子类跟父类不再同一个文件下是不能够使用fileprivate修饰的方法或属性的;且private修饰的方法和属性无论是否跟父类在同一个文件文件内,都无法使用。

      • 在当前文件内新加一个Peson的子类

        class Person:NSObject {
            //MARK: - 存储属性
            fileprivate var name : String = "man"
            private var age : Int = 1
            
            //MARK: - 方法
            fileprivate func privateNameAndAge() {
                print("name:\(name) age:\(age)")
            }
            
            private func setNameAndAge(name:String, age:Int) {
                
            }
        }
        
        class Student : Person {
            func test(){
                //MARK:Person的存储属性
                print("来自Person:\(name)") // Person中 filePrivate 修饰的name可正常访问
              //print("来自Person:\(age)") // Person中 Private 修饰age不可访问
                
                //MARK:Person的方法
                privateNameAndAge() // Person中 filePrivate 修饰的方法可正常访问
                //setNameAndAge("daming",13) //Person中 Private 修饰方法不可访问
            }
        }
      • 在不同文件内新加一个Peson的子类

        //Teacher.swift
        class Teacher: Person {
            func testPrint() {
                //可以访问extension 中的方法,没加修饰默认为 internal
                prinName();
                printAge();
                
                //此时 private和fileprivate 修饰的属性或方法都无法访问到
                //printNameAndAge()
                //setNameAndAge(name: "SuperMan", age: 100)
                
                // let a =  self.age
                //let b =  self.name
            }
            
            //但不可以被重写
            //Declarations from extensions cannot be overridden yet
        //    override func prinName() {
        //        print("子类重写 prinName")
        //    }
        //    override func printAge() {
        //        print("子类重写 printAge")
        //    }
        }
          
  • func:包含用于执行特定任务的代码块。
    class Person {
        var family:String
        var name  :String
        
        init(f:String,n:String) {
            self.family = f
            self.name = n
        }
        
        //MARK: - 特定任务的代码块
        func instructMySelf(){
            print("我的名字叫:\(self.family+self.name)")
        }
    }
    
    let person:Person = Person(f: "", n: "祖儿")
    person.instructMySelf()
  • import:引入一个以独立单元构建的框架或者应用。

    • 基本用法:

      import <#module#>
    • 示例

      import UIKit
      
      //可以使用 UIKit 框架下的所有代码
      class Foo {}
  • init:类、结构体、枚举的实例的初始化准备过程。

    class Person
    {  
        init()  
        {  
            //设置默认值,实例准备被使用
        }  
    } 
  • inout:将一个值传入函数,并可以被函数修改,然后将值传回到调用处,来替换初始值。适用于引用类型和值类型。

    inout是 Swift 中的关键字,可以放置于参数类型前,冒号之后。使用 inout之后,函数体内部可以直接更改参数值,而且改变会保留。

    class Person {
        var family:String
        var name  :String
        
        init(f:String,n:String) {
            self.family = f
            self.name = n
        }
        
        func testOne(r: inout String,f:String,n:String) {
            r = f + n;
        }
    }
    
    var r:String = "1";
    let person = Person(f: "", n: "祖儿");
    person.testOne(r: &r, f: "", n: "祖儿");
    print(r);
    
    结果:宋祖儿

    小结:值类型变量作为参数传入函数,外界和函数参数的内存地址一致,函数内对参数的更改得到了保留。

    需要注意的是:

    1.使用 inout 关键字的函数,在调用时需要在该参数前加上 & 符号

    2.inout 参数在传入时必须为变量,不能为常量或字面量(literal)

    3.inout 参数不能有默认值,不能为可变参数

    4.inout 参数不等同于函数返回值,是一种使参数的作用域超出函数体的方式

    5.多个inout 参数不能同时传入同一个变量,因为拷入拷出的顺序不定,那么最终值也不能确定

  • openpublicinternalfileprivateprivate (访问权限从高到底

    从高到低排序如下:
    open > public > interal > fileprivate > private

    • open
      可以被任何人使用,包括 override 和继承。
    • public
      可以被任何人访问。但其他 module 中不可以被 override 和继承,而在 module 内可以被 override 和继承。

    • internal

      internal 访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。

      如果是框架或者库代码,则在整个框架内部都可以访问,框架由外部代码所引用时,则不可以访问。

      如果是 App 代码,也是在整个 App 代码,也是在整个 App 内部可以访问。

    • fileprivate

      fileprivate 访问级别所修饰的属性或者方法在当前的 Swift 源文件里可以访问。(比如上面样例把 private 改成 fileprivate 就不会报错了)

    • private

      private 访问级别所修饰的属性或者方法只能在当前类里访问。
      (注意:Swift4 中,extension 里也可以访问 private 的属性。)  

  • var:定义可变变量。
    var mutableString = ""  
    mutableString = "Mutated"
  • let:定义一个不可变的变量。
    let constantString = "This cannot be mutated going forward"
  • operator:特殊符号,用于检查、修改、组合值。
    //一元运算符 "-",改变值的符号
    let foo = 5  
    let anotherFoo = -foo //anotherFoo 等于 -5
    
    //二元运算符 "+" 将两个值相加
    let box = 5 + 3
    
    //逻辑运算符 "&&" 将两个布尔值进行组合运算
    if didPassCheckOne && didPassCheckTwo
    
    //三元运算符需要使用三个值
    let isLegalDrinkingAgeInUS:Bool = age >= 21 ? true : false
  • protocol:定义了一组方法、属性或其它要求,用于满足特定任务和一系列功能。
    protocol Blog  {  
        var wordCount:Int { get set }  
        func printReaderStats()  
    }
    class TTIDGPost : Blog  {  
        var wordCount:Int
        init(wordCount:Int) {  
            self.wordCount = wordCount  
        }
        func printReaderStats(){  
            //打印 post 的数据  
        }  
    }
  • static:用于定义类方法,在类型本身进行调用。此外还可以定义静态成员。
    class Person  {  
        var jobTitle:String?
        static func assignRandomName(_ aPerson:Person){  
            aPerson.jobTitle = "Some random job"  
        }  
    }
    
    let somePerson = Person()  
    Person.assignRandomName(somePerson)  
    //somePerson.jobTitle 的值是 "Some random job"
  • struct:通用、灵活的结构体,是程序的基础组成部分,并提供了默认初始化方法。与 class 不同,当 struct 在代码中被传递时,是被拷贝的,并不使用引用计数。除此之外,struct 没有下面的这些功能:
    • 使用继承。
    • 运行时的类型转换。
    • 使用析构方法。
      struct Person  {  
          var name:String  
          var age:Int  
          var gender:String  
      }
  • subscript:访问集合、列表、序列中成员元素的快捷方式。
    • 下标(subscript)在数组和字典中使用,但是你可以给任何类型(枚举,结构体,类)增加 下标subscript 的功能;

    • subscript的语法类似实例方法、计算属性,其本质就是方法;

      struct Person {
          var age = 0
          var no  = 0
          subscript(index: Int) -> Int {
              set {
                  if index == 0 {
                      age = newValue
                  } else {
                      no = newValue
                  }
              }
              get {
                  if index == 0 {
                      return age
                  } else {
                      return no
                  }
              }
          }
      }
      
      var p = Person()
      p[0] = 10
      p[1] = 20
      
      print(p.age)  // 10
      print(p[0])   // 10
      
      print(p.no)  // 20
      print(p[1])  // 20
    • subscript 的返回值类型决定了:

      • get 方法的返回值类型;

      • set 方法中 newValue 的类型;

    • subscript 可以接受多个参数,并且是任意类型;

    • subscript 可以没有 set 方法,但必须要有 get 方法;

    • 如果只有 get 方法,可以省略;

      struct Person {
          var age = 30
          subscript(index: Int) -> Int {
              if index == 0 {
                  return age
              } else {
                  return age * 2
              }
          }
      }
      
      var p = Person()
      print(p[0])  // 30
      print(p[1])  // 60
    • subscript 可以设置参数标签;

      struct Person {
          var age = 30
      
          subscript(index i: Int) -> Int {
              if i == 0 {
                  return age
              } else {
                  return age * 2
              }
          }
      }
      var p = Person()
      print(p[index:0])  // 30
      print(p[index:1])  // 60
    • subscript 可以是类型方法;

      struct Person {
          static subscript(index i: Int) -> Int {
              if i == 0 {
                  return 30
              } else {
                  return 60
              }
          }
      }
      print(Person[index:0])  // 30
      print(Person[index:1])  // 60

       

posted on 2020-09-11 16:09  梁飞宇  阅读(592)  评论(0)    收藏  举报