YongSir

专业程序员伪装者

swift - tip001

swift的tip


  • lazy的使用

lazy 几经更改,最终固定在最简单的形式,加上关键字lazy就行了,还要注意只有变量var才能拥有懒惰特性,因为只有let在实例化调用super.init()之前已经初始化好了,而var是在其后才被索引的,当我们的初始化需要复杂和高昂的代价时,在需要时赋值,就能看出lazy的意义了

同时,lazy并非是线程安全的,如果同时被多个线程同时调用,就不能保证只初始化一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//MARK: lazy
lazy var collectionView: UICollectionView = {
var collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: self.flowLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.registerClass(YRContentCollectionViewCell.self, forCellWithReuseIdentifier: "collection_cell")
collectionView.registerClass(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerView")
return collectionView
}()

lazy var flowLayout:UICollectionViewFlowLayout = {
var collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout.itemSize = CGSizeMake(300, 180)
collectionViewLayout.headerReferenceSize = CGSizeMake(0, 20)
return collectionViewLayout
}()
  • switch
    其实在最开始接触编程时,学习到switch语句,虽说理解起来很简单,但是写起来和编码过程总有种“做的不够”的感觉,绝大多数的语言中switch语句都是跟C中的一样,过了这么多年终于在swift中看到了我所希望的样子

    • 首先是不要一个一个的break了
    • 其次,大大增强Matching类型,事实上可以匹配any data,还可以组合匹配,附加匹配
    • 最后,永远牢记switch必须是完备
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
            switch someValueToConsider {
      case value1:
      statements
      case value2,value3:
      statements
      case value4..<valu5:
      statements
      case (x, y) where x > y:
      statements
      default:
      statements
      }
  • Access Control访问控制权限
    2种级别:

    * 模块区分,如不同的`Framework`之间,而`App bundle`也是一个模块
    
    • 源文件级别,就是各个class的属性和方法级别

      3种访问级别:Public,InternalPrivate

      • Public: 跨模块级别,如Framework中开放的方法和属性
      • 默认访问权限是Internal - 即为同模块,例如iOS开发通常就一个boundle
      • Private: 本源文件级别
1
2
3
4
5
6
7
8
// 修饰class - 模块级别修饰
public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}”

// 修饰方法 - 源文件级别
var someInternalConstant = 0 // 隐式访问级别 internal
private func somePrivateFunction() {}
  • image 和 color 是可以相互转换的
    通常在设置background 时:

    1
    2
    3
    if let patternImage = UIImage(named: "pattern-grey") {
    view.backgroundColor = UIColor(patternImage: patternImage)
    }
  • swift中网络框架Alamofir的使用
    Alamofir的基本使用参照gitHub,已经说的很详细了,这里借助几个开源项目,探求一种最合适的使用方式,先是简单的罗列,最后在分析

    • 一种较为典型的使用,url处理是mattt大神给出的,本身对框架并没有任何变动,只是将API请求和URl做了规范统一,将所有相关统一在一起,并使用枚举增强可读性,eg:

      典型使用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      class ServiceApi: NSObject {
      // API 部分,统一方法一个class中,使用累方法返回完整url
      static var host:String = "http://www.swiftmi.com"

      internal class func getTopicUrl(maxId:Int,count:Int) -> String {

      return "\(host)/api/topic/list2/\(maxId)/\(count)"
      }
      }

      // 所谓的Router枚举, 遵循URLRequestConvertible 协议
      enum Router: URLRequestConvertible {

      }
      ```
      其中的`URLRequestConvertible`协议也是框架中自带的,可见是一种推荐的做法
      ```swift
      // MARK: - URLRequestConvertible

      /**
      Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
      */
      public protocol URLRequestConvertible {
      /// The URL request.
      var URLRequest: NSMutableURLRequest { get }
      }
  • 枚举

    swift中的枚举早已不是C中那个边缘的小tip而已,而是first class类型,强大到令人发质,首先是枚举类型的扩充,从简单的基本类型到元组Tuple都是可以的,可以拥有计算型属性,可以有方法,可以嵌套子枚举,可以嵌套到class和struct中,总之枚举的概念已经被扩展到:

    枚举,用以声明可能状态的有限集,且可以具有附加值。可以通过内嵌(nesting),方法(method),关联值(associated values)和模式匹配(pattern matching),甚至可以遵循协议(protocal),从而让枚举可以分层次地定义任何有组织的数据。

    下面一次性使用Playground的展示其使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    // 1. basic Enum
    enum Movement {
    case Left
    case Right
    case Top
    case Bottom
    }

    let aMovement = Movement.Left

    // - switch
    switch aMovement {
    case .Left: print("left")
    default: ()
    }

    // - if
    if case .Left = aMovement {
    print("left")
    }

    if aMovement == .Left {
    print("left")
    }

    /* 2. Enum Values - 不止是Int型,相比C语言大大的扩展了,对于String和Int你甚至可以使用简些
    枚举中支持以下四种关联值类型:
    整型(Integer)
    浮点数(Float Point)
    字符串(String)
    布尔类型(Boolean)
    */
    enum House: String {
    case Baratheon = "Ours is the Fury"
    case Greyjoy = "We Do Not Sow"
    case Martell = "Unbowed, Unbent, Unbroken"
    case Stark = "Winter is Coming"
    case Tully = "Family, Duty, Honor"
    case Tyrell = "Growing Strong"
    }

    // float double都可以(同时注意枚举中的花式unicode)
    enum Constants: Double {
    case π = 3.14159
    case e = 2.71828
    case φ = 1.61803398874
    case λ = 1.30357
    }

    // swift2.0 的简写
    // 相当于 North = "North", ... West = "West"
    enum CompassPoint: String {
    case Noth, South, East, West
    }


    // 3. 所谓的“读”和“写”
    // - 读: 使用rawValue读取枚举值
    let bestHouse = House.Stark
    print(bestHouse.rawValue)

    // - 写:
    enum Movement002: Int {
    case Left = 0
    case Right = 1
    case Top = 2
    case Bottom = 3
    }

    let rightMovement = Movement002(rawValue: 1) // 创建一个movement.Right 用例,其raw value值为1
    print(rightMovement)

    // 3. Nesting Enums - 枚举中嵌套枚举
    enum Character {
    // 武器
    enum Weapon {
    case Bow
    case Gun
    }
    // 头盔
    enum Helmet {
    case Wooden
    case Iron
    case Diamond
    }

    // 角色
    case Thief
    case Warrior
    case Knight
    }

    let character = Character.Thief
    let weapon = Character.Weapon.Gun
    let helmet = Character.Helmet.Iron

    // 4. Containing Enums - 就是在struct和class中的内嵌枚举
    struct Characters {

    enum CharacterType {
    case Thief
    case Warrior
    case Knight
    }
    // 武器
    enum Weapon {
    case Bow
    case Gun
    }

    let type: CharacterType
    let weapon: Weapon
    }

    let warrior = Characters(type: .Warrior, weapon: .Gun)

    // 5. Associated Value
    // 前边基本都是enum的扩展,在swift中还有对case的扩展
    enum Trade {
    case Buy(stock: String, amount: Int)
    case Sell(stock: String, amount: Int)
    // 可以简写为
    case Steel(String, Int)
    }

    func trade(type: Trade) { }

    // “读”: Pattern Mathching
    let trade = Trade.Buy(stock: "APPLE", amount: 500)
    if case let Trade.Buy(stock, amount) = trade {
    print("buy \(amount) of \(stock)")
    }

    // -- 以下都是大招 --
    // 6. case 可以放元祖
    // 枚举可以有属性property - 不过是计算型属性
    // 可以有方法func
    enum Device {
    case iPad, iPhone
    case AppleWatch

    case Off, On

    // 计算型属性-- Int year
    var year: Int {
    switch self {
    case .iPhone: return 2007
    case .iPad: return 2010
    default: return 0
    }
    }

    // Static Methods - 自动转换规范用语
    static func transSlang(term: String) -> Device? {
    if term == "iWatch" {
    return AppleWatch
    }
    return nil
    }

    // mutating Methods - 可变方法:允许修改case的值
    mutating func screenSwitch() {
    switch self {
    case .On:
    self = Off
    default:
    self = On
    }
    }
    }

    var phoneScreenSwitch = Device.On
    phoneScreenSwitch.screenSwitch()
    phoneScreenSwitch.screenSwitch()
  • swift中JSON解析转Model的实践

第三方框架网络响应返回的,往往是序列化之后的数据,我们需要做的就是根据不同的key进行分割数据,找到合适的数据放到Model 中,但蛋疼的事儿就来了,如何优雅的将数据[往往都是字典]转化为对应的Model呢?
在OC中我们的做法基本上都按照一个流程:通过Model的工厂方法+ modelInitWithDict:(NSDictionay *)dict;,结合KVC实现转化。

Swift中,就不那么方便了,当前的做法是生写,特别的再用到CoreData时,更是只能一个一个的加到模版文件中,而使用ObjectMapper,Argo等第三方框架虽说也行,但在数据处理方面我是一贯持保留意见的,自己想着写一个自己用的脚本,就是根据JSON按格式输出小段重复就行了,这个留待以后再说

  • 属性Property

swift中的Property与OC相比,不单单只是类和实例与value的关联,它还负责枚举和结构体,换言之凡是first class的都是可以有属性的,所以看起来会复杂很多,但同时也做足够细化的分类

* __Stored Properties__: 仅仅存在类与结构体之中                    
* __Computed Properties__ :存在于类,结构体和枚举,功能上不仅仅只是Sotre还要Compute

当属性value发生变化时,可以定义`属性观察者`,来做出反应