玖富钱包分享专题

Swift 拾遗(2)

2017-03-30  本文已影响8人  楼上那位

枚举

定义:

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

}

关联值可以是不同的类型

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来允许子类重写父类的方法实现。

继承

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

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

构造函数

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

两段式构造:
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.

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.

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.

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

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

 init? (...) {
  if notConfirmCondition {return nil }
...
}
required init () {
  ...
}

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

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

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

猜你喜欢

热点阅读