Swift 拾遗(2)

枚举

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

定义:

 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.png
image: ../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.png
image: ../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 方法区分开
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容