Swift高级语法

主要总结一些平时遇到的疑难点,在此总结出来,持续更新。可能有些误导大家的地方,欢迎指正。

难点

  • get,set,willSet,didSet
    🔥get
    计算属性,主要用来计算返回值
    基本语法:

    struct Point {
       var y: Int?
       var x: Int?  
       var loation:(Int?, Int?) { return (x, y) }
    }
    

实现<code>read-only</code>:
Swift中的属性没有对应的实例变量,必须自己创建。
struct Point {
private var _y: Int?
var y:Int { return _y }
}
🔥set
存储属性,主要存储值。
设置存储属性时,必须实现计算属性。

  struct Point {
     var y: Int?
     var x: Int?
     var loation:(Int?, Int?) {
        get { return (x, y) }
        set {
           x = newValue.0
           y = newValue.1
       }
     }
  }

🔥willSet、didSet是观察器
<code>willSet</code>在新值设置之前调用,传入默认参数<code>newValue</code>。
<code>didSet</code>在新值设置之后调用,传入默认参数<code>oldValue</code>。
观察器观察的不止是对象本身,对象的某个属性的值改变也会触发观察器。
⚠️设置观察期后就不能实现计算属性和存储属性。

  • AnyObject, Any, 泛型
    🔥 AnyObject
    定义是一个所有类都遵守的协议,并且适用于OC。相当于OC的<code>id</code>类型,应该是为了兼容Cocoa框架。
    在OC和Swift混编时,很多类型不兼容,就可以<code>AnyObject</code>做过渡。AnyObject也主要是为了兼容OC,Swift编程时使用<code>AnyObject</code>会将类型转换为OC对应的类型,例如123会转为<code>NSNumber而不</code>是<code>Int</code>。
    例如对于Swift中用到的<code>NSArray</code>中的成员,当不知道<code>NSArray</code>中对象的类型时就使用<code>AnyObject</code>。
    🔥 Any
    Any是Swift中定义的类似<code>AnyObject</code>的协议,定义所有类型(包括函数、枚举、结构体等)都遵守的协议。<code>Any</code>的范围比<code>AnyObject</code>更广。
    适用于不知道类型的场景,也使程序更加灵活。
    🔥 泛型
    泛型和<code>Any</code>基本一致,都是为了增加程序的灵活性,但是泛型多了类型检测,可以保证特定类型。
    例如:
    func exchange<T>(x: T, y: T) { // exchange }
  • 协议,optional
    Swift协议不能实现可选。只能通过<code>@objc</code>实现可选。
    但是Swift协议可扩展也可以继承。
    Swift的协议实现一般通过扩展实现,通过协议继承和类的扩展来实现<code>optional</code>。
    协议的命名遵循 Swift 的标准库, 即协议名以 "Type", "-able", "-ible" 结尾。-type 定义行为, -able、-ible 定义元素怎样做某事。
    protocol AppleType {
    func eatApple()
    }
    class Apple {}
    extension Apple: AppleType {
    func eatApple() {}
    }

函数

  • 默认参数
    方法可以通过设置默认参数,实现多参数时参数为空的情况,也可以简写方法,让方法更灵活。

    func abc(a: Int, b: Int? = nil, c: Int?) -> Int {
         return a + (b ?? 0) + (c ?? 0)
    }
    abc(a: 1, c: 2)
    abc(a: 1, b: 1, c: nil)
    
  • 传出参数
    使用<code>inout</code>实现传出参数,但是作为传出参数时,不能使用Optional值。

    func exchange<T>(a: inout T, b: inout T){
         let c = a
         a = b
         b = c
    }
    
    var aa = 10
    var bb = 20
    
    exchange(a: &aa, b: &bb)
    
    aa   // 20 
    bb   // 10
    
  • 省略参数

    func sum(data: Int...) -> Int{
         var result = 0
    
         for item in data {
             result += item
         }
    
         return result
    }
    
    sum(data: 1, 2, 3, 4)
    
  • 泛型方法

    // 自定义带索引的for_in
    func forin<T>(_ items: [T], closure: (T, Int) -> Void) {
         var index = 0
    
         for item in items {
              closure(item, index)
              index += 1
         }
    }
    
    forin([1,3,4]) { item, index in
         print("\(item)....\(index)")
    }
    

Curring

柯里化:《Advanced Swift》中提出的一种通过写方法模板来快速实现方法的机制。让我们的方法更加动态化,可以整合系统方法。

Curring将接受多个参数的函数,转化成每次接受一个参数的调用序列。

Curring主要是一种新的表现形式,主要看个人喜好,并没有什么很特别的地方。

下面介绍两种Curring的方法:

  • 自定义的简单Curring

    //  普通闭包实现map
    let numbers = 1...10
    let result = numbers.map { a in
          return a + 1
    }
    
    // Curring
    func add(_ a: Int) -> (Int) -> Int {
          return { b in return a + b }
    }
    result = numbers.map(add(1))
    
  • 封装的第三方Curring
    Github上已经有一套封装好的Curry方法,可以通过使用这些方法可以快速实现美丽的多参数调用。

    //curry方法实现
    func curry<A, B>(_ function: @escaping (A) -> B) -> (A) -> B {
           return { a in function(a) }
    }
    
    func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
      return  { a in { b in return function(a, b) } }
    }
    
    func curry<A, B, C, D>(_ function: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
      return { a in { b in { c in return  function(a, b, c) }}}
    }
    
    // curry调用
    func addThree(a: Int, b: Int, c: Int) -> Int {
             return a + b + c
    }
    let result = curry(addThree)(1)(2)(3)
    

关键字

  • typealias
    这是一个非常好用的关键字,用来定义别名。系统使用了大量别名。
    可以定义一些有意义的别名,提高可读性。
    也可以定义一些闭包,书写方便。

    typealias Index = Int
    typealias Add = (Int) -> Void
    
    funk printNumber() -> Add {
          return { a in print("\(a)") }
    }
    
    printNumber()(2)
    
  • mutating
    结构体和枚举是值类型,值类型的属性不能在方法中更改,如果需要在方法中修改需要在方向名前面加mutating。

    struct Point {
        var x = 1.0
        mutating func lalala() {
              x += 2
        }
    }
    
  • defer
    延迟执行,附带一个代码块,在方法执行结束前执行该代码块,即时方法执行过程中抛出错误也会执行该代码。多个defer时,执行顺序从后往前执行。

    func testDefer() throws {
             print("1")
             defer { print("2") }
             defer { print("3") }
             throw PrinterError.noFile
             defer { print("4") }
    }
    //打印1.3.2
    
  • required
    用于初始化,required放在init前面。如果子类需要自定义初始化时,必须实现required的初始化。

    class SuperClass {
              required init() {
              }
    }
    
    class SubClass: SuperClass {
               required init() {
               }
    }
    
  • lazy
    lazy关键字的作用是在第一次使用属性的时候才去生成,而不是在一开始就初始化好,按需使用。
    当计算属性的值比较耗时,或者需要外部值的依赖时,用lazy比较合适。
    lazy必须配合var使用,因为let需要有个初始值。
    lazy也是线程不安全的。

    lazy var count: Int = {
             var result = 0
             for i in 0...100 {
                   result += i
             }
             return result
    }()
    
  • 访问权限
    private:只能在当前类里访问
    fileprivate: 当前文件里访问
    internal: 默认访问级别,当前模块或框架可以访问,相当于Target级别
    public: 可以被任何人访问
    open: 可以被任何人访问,包括override和继承

    // 访问权限排序
    open > public > internal > fileprivate > private
    
  • escaping
    @escaping用来标记逃逸闭包。
    在一个函数式的方法中,有一个闭包式的参数,如果这个闭包式的参数在函数中被返回出去了就是逃逸闭包,没有被返回就是非逃逸闭包。

    // 逃逸闭包 
    func closure(input: @escaping () -> ()) -> () -> () {
         return input
    }
    // 非逃逸闭包
    func closure(input: () -> ()) {
         
    }
    
  • ...

闭包

  • 定义

闭包是一个引用类型的代码块,可以用作函数的参数或者返回值,可以捕获上下文的任何常量和变量。

  • 表现形式

全局函数:都是有命名的闭包,但是不能捕获任何值。
嵌套函数:都是有命名的闭包,并且能够捕获当前上下文的值。
闭包表达式:闭包表达式都是无名闭包,可以捕获上下文的值。

  • 捕获上下文

默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。

捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 in 关键字,即使省略了参数名、参数类型和返回类型。

  //值捕获
  var a = 0 
  var b = 0 
  let closure = { [a] in     
     print(a, b)
  } 
  a = 10 
  b = 10 
  closure() // 打印 “0 10”
  //引用捕获
  class SimpleClass {    
       var value: Int = 0
  }
  var x = SimpleClass() 
  var y = SimpleClass() 
  let closure = { [x] in    
       print(x.value, y.value)
  } 
  x.value = 10 
  y.value = 10 
  closure() // 打印 “10 10”
  • 循环引用
    如果捕获列表中的值是引用类型,你可以使用 weak 或者 unowned 来修饰它,闭包会分别用弱引用和无主引用来捕获该值。

    myFunction { print(self.title) }                   // 以强引用捕获
    myFunction { [weak self] in print(self!.title) }   // 以弱引用捕获
    myFunction { [unowned self] in print(self.title) } // 以无主引用捕获
    

在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如:

  // 以弱引用捕获 self.parent 并赋值给 parent
  myFunction { [weak parent = self.parent] in print(parent!.title) }

运算符重载

  1. 定义类

    struct HMPoint {
       var x = 0.0
       var y = 0.0
    

    }

  2. 重载二目运算符

    func +(a: HMPoint, b: HMPoint) -> HMPoint {
       let c = HMPoint(x: a.x + b.x, y: a.y + b.y)
       return c
    }
    
    let va = HMPoint(x: 10, y: 12)
    let vb = HMPoint(x: 23, y: 8)
    
    let vc = va + vb
    vc.x  // 33
    
  3. 重载前缀运算符

    prefix func -(a:HMPoint) -> HMPoint{
        return HMPoint(x: -a.x, y: -a.y)
    }
    let vz = -va
    vz.x //-10
    
  4. 重载后缀运算符

    postfix func ++(a: HMPoint) -> HMPoint {
         return HMPoint(x: a.x + 1, y: a.y+1)
    }
    var vn = vz++
    vn.x // 11
    
  5. 重载+=

    //使用inout改变输入参数
    func +=(left: inout HMPoint, right: HMPoint) {
            left = left + right
    }
    vn += vz
    vn.x
    
  6. 修改运算符优先级

使用<code>infix</code>修改优先级

   infix operator +-: AdditionPrecedence

   extension HMPoint {
        static func +- (left: HMPoint, right: HMPoint) -> HMPoint {
                return HMPoint(x: left.x + right.x, y: left.y - right.y)
        }
   }
   let dassa = vn +- vz
   dassa.x

下标编程

通过<code>subscript</code>关键字可以使普通类同步通过下标访问数据。

struct Subscript {
    var number: Int?
    subscript(index: Int) -> Int {
       return (number ?? 1 ) * index
    }
}

struct Subscript {
      var number: Int?
      subscript(index: Int) -> Int {
          get {
             return (number ?? 1) * index
          }
          set(newValue) {
             number = newValue
          }
     }
}
//带多个值的下标
struct Point {
      subscript(x: Int, y: Int) -> Int {
          return x + y
      }
}
var p = Point()
p[1,3]
//带n个下标
struct Path {
    subscript(x: Int...) -> Int {
        return x.count
    }
}
var path = Path()
path[1,2,3]
//带多维下标
struct Pad {
    var xx: Int
    subscript(x: Int) -> Pad {
        return Pad(xx: x)
    }
}

var pad = Pad(xx: 1)
pad[1][2][3].xx

设计模式

  • 单例模式

    class SomeManager: NSObject {
           static let sharedInstance = SomeManager()
    }
    
  • 原型模式

    protocol Cloning {
         func clone() -> AnyObject
    }
    
    class What: NSObject, Cloning {
            var name: String?
            func clone() -> AnyObject {
                 return What(name: name??"")
           }
    }
    
  • 工厂模式
    protocol Fruit {
    func description() -> String
    }

    class Apple: Fruit {
          func description() -> String {
                return "Apple"
          }
    }
    
    class Banana:Fruit {
          func description() -> String {
               return "Banana"
          }
    }
    
    //工厂模式
    protocol CreateProduct {
           func create() -> Fruit?
           func price() -> CGFloat?
    }
    
    class AppleFactory: CreateProduct  {
          func create() -> Fruit? {
                return Apple()
           }
    
          func price() -> CGFloat? {
                return 3.00
          }
    }
    
    class BananaFactory: CreateProduct {
          func create() -> Fruit? {
                return Banana()
          }
    
          func price() -> CGFloat? {
               return 4.00
          }
    }
    
    enum CreateFactory {
          case Apple
          case Banana
    
          static func createFactory(fruit: CreateFactory) -> CreateProduct {
                switch fruit {
                case .Apple:
                    return AppleFactory()
                case .Banana:
                    return BananaFactory()
                 }        
           }
    }
    
    //测试代码
    let fruit = CreateFactory.createFactory(fruit: .Apple).create()
    print(fruit?.description() ?? "nil")
    

内存管理

//www.greatytc.com/p/f2f47a472947

Runtime

  1. 扩展属性

    extension UIView {
        private struct AssociatedKeys{
           static var loadingViewKey:UIView?
        }
    
        var loadingView: UIView? {
            get {
               return objc_getAssociatedObject(self, &AssociatedKeys.loadingViewKey) as? UIView
            }
            set {
                 if let newValue = newValue {
                     newValue.backgroundColor = .red
                     objc_setAssociatedObject(self, &AssociatedKeys.loadingViewKey, newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                 }
            }
        }
    }
    
  2. 交换方法

    class TestSwizzling : NSObject {
           dynamic func methodOne() -> Int {
               return 1
           }
    }
    
    extension TestSwizzling {
          //在 Objective-C 中,我们在 load() 方法进行 swizzling。但Swift不允许使用这个方法。
          override class func initialize() {
               let originalSelector = #selector(TestSwizzling.methodOne);
               let swizzledSelector = #selector(TestSwizzling.methodTwo);
     
               let originalMethod = class_getInstanceMethod(self, originalSelector);
               let swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
     
               method_exchangeImplementations(originalMethod, swizzledMethod);
               }
    
         func methodTwo() -> Int{
                // swizzling 后, 该方法就不会递归调用
                return methodTwo() + 1
         }
    }
    
    var c = TestSwizzling()
    print(c.methodOne())  //2
    print(c.methodTwo())  //1
    
  3. 获取属性和方法名

    fun allPropertyNamesAndValues(cls:AnyClass) ->[String: AnyObject] {
        var count: UInt32 = 0
        let properties = class_copyPropertyList(cls, &count)
    
        var resultDict: [String: AnyObject] = [:]
        for var i = 0; i < Int(count); ++i {
               let property = properties[i]
     
               // 取得属性名
              let name = property_getName(property)
              if let propertyName = String.fromCString(name) {
              // 取得属性值
                 if let propertyValue = self.valueForKey(propertyName) {
                      resultDict[propertyName] = propertyValue
                 }
             }
        }
       return resultDict
    }
    
    func allMethods() {
         var count: UInt32 = 0
         let methods = class_copyMethodList(Person.self, &count)
    
         for var i = 0; i < Int(count); ++i {
             let method = methods[i]
             let sel = method_getName(method)
             let methodName = sel_getName(sel)
             let argument = method_getNumberOfArguments(method)
     
            print("name: (methodName), arguemtns: (argument)")
         }
    }
    

GCD

  • 队列

    1️⃣主队列VS全局队列
    主队列,即主线程,主要做UI相关操作。
    全局队列,即子线程,主要进行耗时操作和并行操作。

    获取主线程:
    DispatchQueue.main
    获取子线程:
    DispatchQueue.global()
    常用写法:
    DispatchQueue.global().async {
    // 耗时操作
    DispatchQueue.main.sync {
    //UI操作
    }
    }

    2️⃣并行队列VS串行队列
    并行队列:多个任务可以同时执行
    let cQueue = DispatchQueue(label: "name", attributes: .concurrent)
    串行队列:多个任务按顺序执行
    let cQueue = DispatchQueue(label: "name")
    3️⃣队列优先级
    队列与队列之间是并行的,可以通过设置队列优先级改变队列的执行顺序。
    DispatchQueue(label: "a", qos: .background).async {
    print(1)
    }

    DispatchQueue(label: "a", qos: .default).async {
          print(2)
      }
      
    DispatchQueue(label: "a", qos: .unspecified).async {
          print(3)
      }
     // 打印结果
     3
     2
     1
    
  • 任务

    1️⃣同步任务
    阻塞当前线程,任务执行完线程才会继续执行
    conQ.sync {
    // 同步操作
    }
    2️⃣异步任务
    线程直接往下执行,不阻塞当前线程
    conQ.async {
    // 异步操作
    }

  • 延时
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
    // 延时任务
    })

  • 线程安全

单元测试

同OC
第三方库中都有很全面的单元测试代码,可以学习一下。

第三方库

  • SnapKit
    自动布局约束库
  • SwiftyJson
    将对象转化为Json处理
  • Alamofire
    网络库
  • RxSwift
    函数式编程
  • ObjectMapper
    同SwiftyJson
  • Quick
    行为驱动开发框架
  • Eureka
    快速构建 iOS 各种复杂表单的库
  • Spring
    封装的系统动画库
  • Kingfisher
    图片缓存库
  • CoreStore
    Core Data管理类库
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容