Swift 拾遗(2)

作者: 楼上那位 | 来源:发表于2017-03-30 19:03 被阅读8次

    枚举

    • 可以提供关联值
    • 定义计算属性
    • 实例方法 构造函数
    • 遵循协议

    定义:

     enum BarStyle {
     case  BarRed,BarBlue
    }
    // 设置关联值
    enum BarTitle {
    case BarTitleShow (Bool)
    case BarStyle(Dictionary)
    ...
    
    }
    

    关联值可以是不同的类型

    • 原始值
      枚举成员可以设置默认值,且这些case 的原始值得类型必须是相同的
    enum ASCIIControlCharacter:Character {
    case tab = '\t'
    case line = ''
    case whiteSpace = ' '
    ...
    }
    

    原始值的隐式赋值
    如果原始值得类型是String 或者整数,不要显示的为每一个枚举成员设置原始值,Swift 将自动设置。整型自动加一,字符串默认是枚举成员的String 名称

    结构体和类

    • 类是引用类型,结构体是值类型
    • 类允许继承,结构体不允许
    • 类的类型转换允许在运行时进行检查
    • 结构体有逐一成员构造函数,类没有

    恒等运算符 === !== 用于判断两个变量是否引用同一个类实例

    属性

    1. 存储属性
      ** 延迟存储属性声明必须使用var ,因为属性的初始值有可能在实例的构造函数完成之后才会得到,只有在第一次访问的时候才会被创建,而常量的初始值在构造函数完成之前就必须要有初始值**

    可选类型

    使用 ? 或者 ! 修饰的变量,默认值均为 nil ,两者的区别只是需不需要隐式解析

    循环引用

    遇到循环引用就会想到 weakunowned,两者有何区别呢?

    1. 计算属性
      不存储值,只提供 gettersetter方法
    struct React {
    // 这里容易和 闭包赋值产生混淆,var center :Point = {}();这是闭包赋值
     var center:Point {
       get { ... }
       set { newValue ... }
      }
    }
    

    只有 getter 没有 setter 的计算属性就是只读计算属性

    3 . 属性观察器 willSet , didSet

    父类的属性在子类的构造器中被赋值时,它在父类中的willSet ,didSet 观察器会被调用,随后才会调用子类的观察器。在副类的初始化函数调用之前,子类给属性赋值,观察器不会被触发

    class StepCounter { 
    var totalSteps: Int = 0 {
         willSet(newTotalSteps) {
                 print("About to set totalSteps to \(newTotalSteps)")
       }
     didSet {
           if totalSteps > oldValue {
           print("Added \(totalSteps - oldValue) steps")
          }
       }
      }
    }
    

    4 . 类型属性
    使用关键字 static 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字class 来支持子类对父 类的实现进行重写。

    struct SomeStructure {
        static var storedTypeProperty = "Some value." 
        static var computedTypeProperty: Int {
             return 1 
        }
     }
    

    再看一个完整的例子

    struct AudioChannel {
      static let thresholdLevel = 10 
      static var maxInputLevelForAllChannels = 0 
       var currentLevel: Int = 0 { 
           didSet { 
             if currentLevel > AudioChannel.thresholdLevel { 
    // 将当前音量限制在阀值之内
                currentLevel = AudioChannel.thresholdLevel
            } 
           if currentLevel > AudioChannel.maxInputLevelForAllChannels         { // 存储当前音量作为新的最大输入音量 AudioChannel.maxInputLevelForAllChannels = currentLevel
     }}
    }}
    

    方法

    在方法的 func 关键字之前加上关键字static ,来指定类型方法。类还可以用关键字 class来允许子类重写父类的方法实现。

    继承

    • 值类型不支持继承
      重写继承过来的实力属性或者类型属性,提供自己在定制的setter getter 方法,或者添加属性观察器,观察重写的属性值得改变

    子类并不知道继承来的属性是存储还是计算,只知道一个名字和类型,所以在重写的时候必须指出类型和名字。
    子类可以将一个只读重写为读写,但是反之不可。

    你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置 的,所以,为它们提供 willSet 或 didSet 实现是不恰当。 此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已 经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。

    构造函数

    • 如果结构体或者类的所有属性都有默认值,同时没有自定义的构造函数,Swift会提供一个默认的构造函数
      如果提供了自定义的构造函数,系统就不再提供默认的

    假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器 写到扩展( extension )中,而不是写在值类型的原始定义中。

    • 指定构造函数必须向上调用构造函数(调用父类)
    • 便利构造函数必须横向调用其他构造函数


      initializerDelegation02_2x.png

    两段式构造:
    1 . 初始化存储属性
    2 . 实例准备使用之前进一步定制存储属性的值

    类型检查的四个阶段:

    1 . 指定构造函数必须保证目前所在类引入的所有属性先初始化,再调用父类的构造函数
    2 . 调用父类构造函数后再为继承的属性赋新值
    3 . 便利构造函数必须调用同一类的其他构造函数,再为任意属性赋值
    4 . 构造函数在第一阶段未完成之前不能使用任何实例方法,实例属性以及self

    构造阶段
    阶段1 :
    • A designated or convenience initializer is called on a class.
      Memory for a new instance of that class is allocated. The memory is not yet initialized.
    • A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
    • The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
    • This continues up the class inheritance chain until the top of the chain is reached.

    Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

    阶段2:
    • Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self
      and can modify its properties, call its instance methods, and so on.
    • Finally, any convenience initializers in the chain have the option to customize the instance and to work with self

    Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:


    image: ../Art/twoPhaseInitialization01_2x.pngimage: ../Art/twoPhaseInitialization01_2x.png

    In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.
    The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.
    The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.
    As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.
    Here’s how phase 2 looks for the same initialization call:


    image: ../Art/twoPhaseInitialization02_2x.pngimage: ../Art/twoPhaseInitialization02_2x.png
    The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).
    Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).

    Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.

    • 构造函数的继承和重写
      与OC 不同,Swift 中的子类默认(符合特定条件是ok的)不会继承父类的构造函数 (不代表不能super 调用父类构造函数哦哦😯)

    当你重写一个父类的指定构造器时,你总是需要写override 修饰符,即使你的子类将父类的指定构造器重写为了便利构造函数。

    如果写了一个与父类的便利构造函数相匹配的子类构造函数,由于子类不能直接调用父类的便利构造函数,因此严格意义上来说,你写的子类并未对父类构造函数提供重写行为,所以不需要添加override 修饰符

    • 继承父类构造函数的条件
      1 . 子类没有定义任何指定构造函数,他将自动继承所有父类的指定构造函数
      2 . 如果子类提供了所有父类指定构造函数--无论是=通过规则1 继承过来的还是提供了自定义实现,子类将自动继承父类的所有便利构造函数(另外子类可以将父类的指定构造函数实现为便利构造函数)

    • 可失败构造函数

     init? (...) {
      if notConfirmCondition {return nil }
    ...
    }
    
    • 必要构造函数 -- 必须实现的
    required init () {
      ...
    }
    

    通过闭包或者函数设置属性的默认值

    如果某个存储属性的默认值需要一些定制或者设置,可以使用闭包或者全局函数提供定制,每当实例创建时,对应的闭包或者函数就会被调用

    使用闭包
    class SomeClass {
      let someProperty :someType = {
        ...
       return someValue
     }()
    // 注意这里的`()` 如果忽略,则将闭包赋值给属性
    注意和计算属性getter 方法区分开
    }
    

    相关文章

      网友评论

        本文标题:Swift 拾遗(2)

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