dis执行一次 swift_dis 命令 swift - CSDN
精华内容
参与话题
  • Swift 代码调试核武-LLDB调试基础

    千次阅读 2016-01-10 14:55:04
    我的stackoverflow前言:LLDB是个开源的调试器,与XCode绑定的LLDB的使用中,Swift与Objective C还是有一些差别的本文主要侧重LLDB的常用命令资料(目前状态XCode 7.2 Swift 2.1.1),非XCode 7.2+本文代码可能不

    原创Blog,转载请注明出处
    http://blog.csdn.net/hello_hwc?viewmode=list
    我的stackoverflow

    profile for Leo on Stack Exchange, a network of free, community-driven Q&A sites


    前言:LLDB是个开源的调试器,与XCode绑定的

    LLDB的使用中,Swift与Objective C还是有一些差别的

    本文主要侧重LLDB的常用命令


    资料(目前状态XCode 7.2 Swift 2.1.1),非XCode 7.2+本文代码可能不能运行

    对了,Swift到现在也不过一岁半,所以LLDB对于Swift的支持肯定不如OC那么好。


    如何打开LLDB如何使用?

    通常的方式就是断点,另外,关于利用XCode图形化调试,在我这篇文章里有详细讲解

    本文适合XCode 7.2 +
    不管是在程序中加断点

    还是手动的暂停程序


    让代码停在Swift Error 或者Objective C异常

    停在Objective C异常

    (lldb) br s -E  objc
    Breakpoint 6: where = libobjc.A.dylib`objc_exception_throw, address = 0x000000010dededbb
    

    停在Swift Error

    (lldb) br s -E swift
    Breakpoint 7: where = libswiftCore.dylib`swift_willThrow, address = 0x000000010e55ccc0

    停在某一种类型的Swift Error

    (lldb) br s -E swift -O EnumError
    Breakpoint 8: where = libswiftCore.dylib`swift_willThrow, address = 0x000000010e55ccc0

    以此作为开端,希望读者能耐心看完,本文很长


    准备工作

    为了方便,我们先写好这样的一个Model类,定义个ErrorType,并且定义个实例方法抛出异常

    enum CustomError:ErrorType{
        case LeoError1
        case LeoError2
    }
    class Person:NSObject{
        var name:String
        var age:UInt32
        init(name:String,age:UInt32){
            self.name = name
            self.age = age
        }
        //重写description是为了方便调试
        override var description:String{
            return "name:\(name) age:\(age)"
        }
        func PersonException() throws{
            throw CustomError.LeoError1
        }
    }

    然后在viewDidLoad中初始化一个对象,并打一个断点

    let person = Person(name: "Leo", age: 23)

    打印命令 p/po

    p

    (lldb) p person
    (SwiftLLDBDemo.Person) $R0 = 0x00007f99b8d30b40 {
      ObjectiveC.NSObject = {
        isa = SwiftLLDBDemo.Person
      }
      name = "Leo"
      age = 23
    }
    

    po

    (lldb) po person
    name:Leo age:23

    p命令会打印出对象的类型,如果是Objective C对象,会打印出isa,以及属性的值
    po 命令 对于继承自NSObject得对象,指示会打印出description中的内容

    再举个例子

    (lldb) po ["123","345"]
    ▿ 2 elements
      - [0] : "123"
      - [1] : "345"
    
    (lldb) p ["123","345"]
    ([String]) $R2 = 2 values {
      [0] = "123"
      [1] = "345"
    }

    也可以,调用一段代码

    (lldb) p for i in 1...3{ print(i) }
    1
    2
    3

    执行代码 e

    expression命令可以帮助我们执行代码,

    (lldb) e person.name = "Jack"
    (lldb) p person
    (SwiftLLDBDemo.Person) $R1 = 0x00007f9bf1424c20 {
      ObjectiveC.NSObject = {
        isa = SwiftLLDBDemo.Person
      }
      name = "Jack"
      age = 23
    }

    在这我们深入的研究所有命令

    (lldb) help

    内容太多,不拷贝进来了,自己在XCode里敲敲试试吧。

    可以看到,有一部分是alias部分,有linux经验的同学应该知道,alias就是将一个简单的命令设置为复杂命令的别名。我们上面讲到的两个命令p/po就是两个alias命令

      p         -- ('expression --')  Evaluate an expression (ObjC++ or Swift) in
                   the current program context, using user defined variables and
                   variables currently in scope.
      po        -- ('expression -O  -- ')  Evaluate an expression (ObjC++ or Swift)
                   in the current program context, using user defined variables and
                   variables currently in scope.

    所以,p命令,本质是expression -- ;po命令,本质是expression -O --

    (lldb) expression -- person
    (SwiftLLDBDemo.Person) $R0 = 0x00007f96a3f792d0 {
      ObjectiveC.NSObject = {
        isa = SwiftLLDBDemo.Person
      }
      name = "Leo"
      age = 23
    }
    (lldb) expression -O -- person
    name:Leo age:23

    更加复杂的执行

    例如,动态的修改view的背景色

    e self.view.backgroundColor = UIColor.blueColor

    Expression命令很灵活

    格式化打印 -f (format)

    (lldb) expr -f bin -- person.age 
    (UInt32) $R2 = 0b00000000000000000000000000010111
    (lldb) expr -f oct -- person.age
    (UInt32) $R3 = 027
    (lldb) expr -f hex -- person.age
    (UInt32) $R4 = 0x00000017

    格式化打印可以更简单

    (lldb) p/x person.age
    (UInt32) $R5 = 0x00000017

    打印Raw value expr -R -- 变量

    (lldb) expr -R -- person
    (SwiftLLDBDemo.Person) $R6 = 0x00007f96a3f792d0 {
      ObjectiveC.NSObject = {}
      name = {
        _core = {
          _baseAddress = {
            _rawValue = 0x0000000101041298 "Leo"
          }
          _countAndFlags = {
            value = 3
          }
          _owner = None {
            Some = {
              instance_type = 0x0000000000000000
            }
          }
        }
      }
      age = {
        value = 23
      }
    }

    显示变量类型expr -T -- 变量

    (lldb) expr -T -- person
    (SwiftLLDBDemo.Person) $R7 = 0x00007f96a3f792d0 {
      (NSObject) ObjectiveC.NSObject = {
        (Class) isa = SwiftLLDBDemo.Person
      }
      (String) name = "Leo"
      (UInt32) age = 23
    }

    当然也可以结合多个使用expr -RRT -- person

    帮助命令,可以再深入看看其他执行选项,不过不是很常用

     help expression

    LLDB变量

    LLDB的变量和脚本语言类似,以美元符号开头,使用的时候也要带着美元符号,调用的时候和Swift的语法一致
    例如

    (lldb) e var $a = 10
    (lldb) p $a
    (Int) $a = 10
    
    (lldb) e var $b = Person(name: "Jack", age: 25)
    (lldb) p $b
    (SWTest.Person) $b = 0x00007f8909539080 {
      ObjectiveC.NSObject = {
        isa = SWTest.Person
      }
      name = "Jack"
      age = 25
    }

    断点

    断点采用这个命令breakpoint,缩写br
    断点命令的文档较少,这里先列出文档

    (lldb) help breakpoint
    The following subcommands are supported:
    
          clear   -- Clears a breakpoint or set of breakpoints in the executable.
          command -- A set of commands for adding, removing and examining bits of
                     code to be executed when the breakpoint is hit (breakpoint
                     'commands').
          delete  -- Delete the specified breakpoint(s).  If no breakpoints are
                     specified, delete them all.
          disable -- Disable the specified breakpoint(s) without removing them.  If
                     none are specified, disable all breakpoints.
          enable  -- Enable the specified disabled breakpoint(s). If no breakpoints
                     are specified, enable all of them.
          list    -- List some or all breakpoints at configurable levels of detail.
          modify  -- Modify the options on a breakpoint or set of breakpoints in
                     the executable.  If no breakpoint is specified, acts on the
                     last created breakpoint.  With the exception of -e, -d and -i,
                     passing an empty argument clears the modification.
          name    -- A set of commands to manage name tags for breakpoints
          set     -- Sets a breakpoint or set of breakpoints in the executable.

    breakpoint set -E language
    breakpoint s -E swift -O ErrorType

    列出断点

    (lldb) br li
    Current breakpoints:
    1: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 27, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 75 at ViewController.swift:27, address = 0x000000010ef8556b, resolved, hit count = 1 

    禁用断点

    (lldb) br dis 1
    1 breakpoints disabled.

    删除断点

    (lldb) br del 1
    1 breakpoints deleted; 0 breakpoint locations disabled.
    (lldb) br li
    No breakpoints currently set.

    添加断点

    (lldb) br set -f ViewController.swift -l 28
    Breakpoint 2: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 139 at ViewController.swift:29, address = 0x000000010ef855ab

    也可以简写为

    (lldb) b ViewController.swift:28
    Breakpoint 3: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 139 at ViewController.swift:29, address = 0x000000010ef855ab
    (lldb) br li
    Current breakpoints:
    2: file = 'ViewController.swift', line = 28, locations = 1, resolved = 1, hit count = 0
      2.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 139 at ViewController.swift:29, address = 0x000000010ef855ab, resolved, hit count = 0 
    
    3: file = 'ViewController.swift', line = 28, locations = 1, resolved = 1, hit count = 0
      3.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 139 at ViewController.swift:29, address = 0x000000010ef855ab, resolved, hit count = 0 

    断点Name

    断点名称是用来管理一组断点的,通过name可以启用或者禁用一组断点,一个断点可以有多个name,很像tag。

    br set -f ViewController.swift -l 28 -N leo
    (lldb) br set -f ViewController.swift -l 28 -N leo
    Breakpoint 2: 3 locations.
    (lldb) br li
    Current breakpoints:
    1: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 34, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 75 at ViewController.swift:34, address = 0x00000001070d51fb, resolved, hit count = 1 
    
    2: file = 'ViewController.swift', line = 28, locations = 3, resolved = 3, hit count = 0
      Names:
        leo
    
      2.1: where = SWTest`SWTest.ViewController.__deallocating_deinit + 12 at ViewController.swift, address = 0x00000001070d538c, resolved, hit count = 0 
      2.2: where = SWTest`SWTest.ViewController.init (SWTest.ViewController.Type)(nibName : Swift.Optional<Swift.String>, bundle : Swift.Optional<__ObjC.NSBundle>) -> SWTest.ViewController + 57 at ViewController.swift, address = 0x00000001070d53f9, resolved, hit count = 0 
      2.3: where = SWTest`SWTest.ViewController.init (SWTest.ViewController.Type)(coder : __ObjC.NSCoder) -> Swift.Optional<SWTest.ViewController> + 16 at ViewController.swift, address = 0x00000001070d56d0, resolved, hit count = 0 

    在加上名字以后,我们就可以通过名字来操作断点了

    (lldb) br disable -N leo
    1 breakpoints disabled.

    在某一个函数上加断点

    例如,在准备Person的函数中personLoop添加断点

    (lldb) br s -F personLoop
    Breakpoint 2: 2 locations.
    (lldb) br li
    Current breakpoints:
    1: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 40, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 75 at ViewController.swift:40, address = 0x000000010f6d51bb, resolved, hit count = 1 
    
    2: name = 'personLoop', locations = 2, resolved = 2, hit count = 0
      2.1: where = SWTest`SWTest.Person.personLoop (SWTest.Person)() -> () + 12 at ViewController.swift:25, address = 0x000000010f6d45cc, resolved, hit count = 0 
      2.2: where = SWTest`@objc SWTest.Person.personLoop (SWTest.Person)() -> () + 4 at ViewController.swift, address = 0x000000010f6d4664, resolved, hit count = 0 
    

    条件断点

    先在这里加一个断点
    这里写图片描述

    当某种条件产生的时候触发的断点,例如停在1000次循环的第800次

    然后添加条件,停在i == 80

    (lldb) br li
    Current breakpoints:
    1: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 40, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 75 at ViewController.swift:40, address = 0x00000001094d81bb, resolved, hit count = 1 
    
    2: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 27, locations = 1, resolved = 1, hit count = 1
    
      2.1: where = SWTest`SWTest.Person.personLoop (SWTest.Person)() -> () + 111 at ViewController.swift:27, address = 0x00000001094d762f, resolved, hit count = 1 
    
    (lldb) br modify -c 'i == 80'
    (lldb) po i
    80
    

    断点行为

    当断点触发的时候,执行的LLDB代码

    例如,当i==80的时候,输出sum

    (lldb) br li
    Current breakpoints:
    1: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 40, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = SWTest`SWTest.ViewController.viewDidLoad (SWTest.ViewController)() -> () + 75 at ViewController.swift:40, address = 0x00000001057391bb, resolved, hit count = 1 
    
    3: file = '/Users/huangwenchen/Desktop/SWTest/SWTest/ViewController.swift', line = 27, locations = 1, resolved = 1, hit count = 0
    
      3.1: where = SWTest`SWTest.Person.personLoop (SWTest.Person)() -> () + 111 at ViewController.swift:27, address = 0x000000010573862f, resolved, hit count = 0 
    
    (lldb) br modify -c 'i == 80' 3
    (lldb) br command add 3
    Enter your debugger command(s).  Type 'DONE' to end.
    > po sum
    > DONE
     po sum
    3160

    其他断点相关

    lldb中输入

    (lldb) help br set

    篇幅限制,不列出help的结果了


    类型查找Type

    抛出一个异常,不知道是啥的时候怎么办?

    (lldb) type lookup CustomError
    enum CustomError : ErrorType {
      case LeoError1
      case LeoError2
      var hashValue: Swift.Int {
        get {}
      }
      var _code: Swift.Int {
        get {}
      }
    }

    可以简写

    (lldb) ty l CustomError
    enum CustomError : ErrorType {
      case LeoError1
      case LeoError2
      var hashValue: Swift.Int {
        get {}
      }
      var _code: Swift.Int {
        get {}
      }
    }

    LLDB中的异常处理

    执行一个抛出异常的方法

    (lldb) e person.personException()
    (SWTest.CustomError) $E0 = LeoError1

    流程控制

    frame info 查看当前所在的位置

    (lldb) frame info
    frame #0: 0x000000010573862f SWTest`SWTest.Person.personLoop (self=0x00007fbaf1d054d0)() -> () + 111 at ViewController.swift:27
    • c 继续运行
    • n next 下一行
    • s step in
    • finish step out

    函数提前返回

    thread return

    通过这个命令,可以很好的隔离函数,通常用在函数的最开始位置,避免ARC引用计数问题


    最后

    欢饮关注我的CSDN博客,很长一段时间内,我会持续的更新iOS/Swift/Objective C相关的文章

    LLDB调试的文章应该还有一篇进阶使用的,近期会更新


    展开全文
  • Swift 2.2 基础语法

    千次阅读 2016-05-19 22:42:15
    Swift 介绍简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序 2014 年,在 Apple WWDC 发布 几家欢喜,几家愁 愁者:只学Object-C的人 欢喜者:之前做过java/python/js语言的人 历史 2010 年 7...

    Swift 介绍

    简介

    • Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序
    • 2014 年,在 Apple WWDC 发布
      • 几家欢喜,几家愁
      • 愁者:只学Object-C的人
      • 欢喜者:之前做过java/python/js语言的人

    历史

    • 2010 年 7 月,苹果开发者工具部门总监 Chris Lattner 开始着手 Swift 编程语言的设计工作
    • 用一年时间,完成基本架构
    • Swift 大约历经 4 年的开发期,2014 年 6 月发表
    • 克里斯·拉特纳何许人?LLVM 项目的主要发起人与作者之一Clang 编译器的作者苹果公司『开发者工具』部门的主管领导Xcode、Instruments等编译器团队Swift的大部分基础架构均由他1人完成评价:大神中的大神牛逼中的牛逼

    特点

    • 特点
      • 从它的语法中能看到Objective-C、JavaScript、C#、Python等语言的影子
      • 语法简单、代码简洁、使用方便
      • 可与Objective-C混合使用(相互调用)
      • 提供了类似 Java 的名字空间(namespace)、泛型(generic)、运算对象重载(operator overloading)
    • 为什么设计Swift语言
      • 让应用开发更简单、更快、更稳定
      • 确保最终应用有着更好的质量

    重要性

    • 苹果目前在大力推广Swift
    • 斯坦福大学的公开课目前也是使用Swift在授课.因为以后Swift必将代替OC
    • 题外话:我们同学去面试,面试官问是否会Swift,如果会,我们下个项目直接用Swift来写.你可以教我们Swift.
    • 个人建议:
      • 先掌握Swift最基本的语法
      • 高级/特殊的功能随着学习的深入再深入研究
      • 千万不要浮躁(前面班级经验)
      • Swift并不难
      • 但是语法和OC区别非常非常大
      • 如果是一个听一听,听不懂就算了的心态.一定是学不好的
      • 如果想要学习,就认真听讲,好好练习
        img

    资源网站

    Swift初体验

    • Playground是什么?
      • 从Xcode6开始出现(Swift开始出现)
      • 翻译为:操场/游乐场
      • 对于学习Swift基本语法非常方便
      • 所见即所得(快速查看结果)
      • 语法特性发生改变时,可以快速查看.
    • Swift最基本的语法变化
      • 导入框架 import UIKit
      • 定义标识符时,必须声明该标识符是变量还是常量
      • 声明标识符的格式:变量/常量关键字 名称 : 数据类型
      • 语句结束时不需要加;
      • 如果同一行有多个语句,则依然需要加
      • 但是不建议一行多条语句
      • Swift中的打印语句:print(打印的内容)

    常量&变量

    什么是常量和变量

    • 在Swift中规定:在定义一个标识符时必须明确说明该标识符是一个常量还是变量
    • 使用let来定义常量,定义之后不可以修改
    • 使用var来定义变量,定义之后可以修改

    变量的基本使用

    import UIKit
    
    let a : Int = 10
    // 错误写法,当一个字段定义为常量时是不可以修改的
    // a = 20
    
    var b : Int = 20
    // 因为b定义为变量,因此是可以修改的
    b = 30

    常量和变量的使用注意:

    • 注意:

      • 在真实使用过程中,建议先定义常量,如果需要修改再修改为变量(更加安全)
      • 是指向的对象不可以再进行修改.但是可以通过指针获得对象后,修改对象内部的属性
      // 注意:声明为常量不可以修改的意思是指针不可以再指向其他对象.但是可以通过指针拿到对象,修改其中的属性
      // view : UIView = [[UIView alloc] init];
      // Swift对象中不需要*
      var view : UIView = UIView()
      view = UIView()
      
      let view1 : UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
      view1.backgroundColor = UIColor.redColor()
      
      // 枚举类型的用法:类型.枚举的值
      let btn : UIButton = UIButton(type: UIButtonType.Custom)
      btn.backgroundColor = UIColor.blueColor()
      btn.setTitle("按钮", forState: UIControlState.Normal)
      btn.frame = CGRect(x: 20, y: 20, width: 60, height: 30)
      view1.addSubview(btn)

    Swift中数据类型

    Swift类型的介绍

    • Swift中的数据类型也有:整型/浮点型/对象类型/结构体类型等等
    • 先了解整型和浮点型
    • 整型
      • 有符号
      • Int8 : 有符号8位整型
      • Int16 : 有符号16位整型
      • Int32 : 有符号32位整型
      • Int64 : 有符号64位整型
      • Int : 和平台相关(默认,相当于OC的NSInteger)
      • 无符号
      • UInt8 : 无符号8位整型
      • UInt16 : 无符号16位整型
      • UInt32 : 无符号32位整型
      • UInt64 : 无符号64位整型
      • UInt : 和平台相关(常用,相当于OC的NSUInteger)(默认)
    • 浮点型

      • Float : 32位浮点型
      • Double : 64浮点型(默认)
      // 定义一个Int类型的变量m,并且赋值为10
      var m : Int = 10
      // 定义一个Double类型的常量n,并且赋值为3.14
      let n : Double = 3.14

    Swift中的类型推导

    • Swift是强类型的语言
    • Swift中任何一个标识符都有明确的类型
    • 注意:

      • 如果定义一个标识符时有直接进行赋值,那么标识符后面的类型可以省略.
      • 因为Swift有类型推导,会自动根据后面的赋值来决定前面的标识符的数据类型
      • 可以通过option+鼠标左键来查看变量的数据类型 
      // 定义变量时没有指定明确的类型,但是因为赋值给i一个20.20为整型.因此i为整型
      var i = 20
      // 错误写法:如果之后赋值给i一个浮点型数值,则会报错
      // i = 30.5
      
      // 正确写法
      var j = 3.33
      j = 6.66

    Swift中基本运算

    • Swift中在进行基本运算时必须保证类型一致,否则会出错
      • 相同类型之间才可以进行运算
      • 因为Swift中没有隐式转换
    • 数据类型的转化

      • Int类型转成Double类型:Double(标识符)
      • Double类型转成Int类型:Int(标识符)
      let a = 10
      let b = 3.14
      
      // 错误写法
      // let c = a + b
      // let c = a * b
      
      // 正确写法
      let c = Double(a) + b
      let d = a + Int(b)

    逻辑分支

    一. 分支的介绍

    • 分支即if/switch/三目运算符等判断语句
    • 通过分支语句可以控制程序的执行流程

    二. if分支语句

    • 和OC中if语句有一定的区别

      • 判断句可以不加()
      • 在Swift的判断句中必须有明确的真假
      • 不再有非0即真
      • 必须有明确的Bool值
      • Bool有两个取值:false/true
        // 演练一:
        let a = 10
      
        // 错误写法:
        //if a {
        //    print("a")
        //}
      
        // 正确写法
        if a > 9 {
            print(a)
        }
      
        // 演练二:
        let score = 87
      
        if score < 60 {
            print("不及格")
        } else if score <= 70 {
            print("及格")
        } else if score <= 80 {
            print("良好")
        } else if score <= 90 {
            print("优秀")
        } else {
            print("完美")
        }
      
        // 演练三:
        // 这个是可选类型,因为只有声明成可选类型后,才可以判断是否为空
        // 可选类型会在后续讲解,可先了解即可
        let view : UIView? = UIView()
      
        // 判断如果view有值,则设置背景
        // 错误写法
        //if view {
        //    view.backgroundColor = UIColor.redColor()
        //}
      
        if view != nil {
            view!.backgroundColor = UIColor.redColor()
        }

    三. 三目运算符

    • Swift 中的 三目 运算保持了和 OC 一致的风格

      var a = 10
      var b = 50
      
      var result = a > b ? a : b
      println(result)

    四.guard的使用

    • guard是Swift2.0新增的语法
    • 它与if语句非常类似,它设计的目的是提高程序的可读性
    • guard语句必须带有else语句,它的语法如下:

      • 当条件表达式为true时候跳过else语句中的内容,执行语句组内容
      guard 条件表达式 else {
          // 条换语句
          break
      }
      语句组
    • 例子

      var age = 18
      
      func online(age : Int) -> Void {
          guard age >= 18 else {
              print("回家去")
              return
          }
      
          print("可以上网")
      }
      
      online(age)

    四.switch分支

    switch的介绍
    • Switch作为选择结构中必不可少的语句也被加入到了Swift中
    • 只要有过编程经验的人对Switch语句都不会感到陌生
    • 但苹果对Switch进行了大大的增强,使其拥有其他语言中没有的特性
    switch的简单使用
    • 基本用法和OC用法一致
    • 不同之处:
      • switch后可以不跟()
      • case后可以不跟break(默认会有break)
    • 例子:

      let sex = 0
      
      switch sex {
      case 0 :
          print("男")
      case 1 :
          print("女")
      default :
          print("其他")
      }
    • 简单使用补充:

      • 一个case判断中,可以判断多个值
      • 多个值以,隔开
      let sex = 0
      
      switch sex {
      case 0, 1:
          print("正常人")
      default:
          print("其他")
      }
    • 简单使用补充:

      • 如果希望出现之前的case穿透,则可以使用关键字fallthrough
      let sex = 0
      
      switch sex {
      case 0:
          fallthrough
      case 1:
          print("正常人")
      default:
          print("其他")
      }
    Switch支持多种数据类型
    • 浮点型的switch判断

      let f = 3.14
      switch f {
      case 3.14:
          print("π")
      default:
          print("not π")
      }
    • 支持字符串类型

      • 字符串的使用后面会详细讲解
      let m = 5
      let n = 10
      var result = 0
      
      let opration = "+"
      
      switch opration {
          case "+":
              result = m + n
          case "-":
              result = m - n
          case "*":
              result = m * n
          case "/":
              result = m / n
      default:
          result = 0
      }
      
      print(result)
    switch支持区间判断
    • 什么是区间?
      • 通常我们指的是数字区间:0~10,100~200
    • swift中的区间常见有两种

      • 半开半闭区间:0..<10 表示:0~9,不包括10
      • 闭区间:0…10 表示:0~10
      let score = 88
      
      switch score {
      case 0..<60:
          print("不及格")
      case 60..<80:
          print("几个")
      case 80..<90:
          print("良好")
      case 90..<100:
          print("优秀")
      default:
          print("满分")
      }

    循环的介绍

    • 在开发中经常会需要循环
    • 常见的循环有:for/while/do while.
    • 这里我们只介绍for/while,因为for/while最常见

    for循环的写法

    • 最常规写法

      // 传统写法
      for var i = 0; i < 10; i++ {
          print(i)
      }
    • 区间for循环

      for i in 0..<10 {
          print(i)
      }
      
      for i in 0...10 {
          print(i)
      }
    • 特殊写法

      - 如果在for循环中不需要用到下标i
      for _ in 0..<10 {
          print("hello")
      }

    while和do while循环

    • while循环

      • while的判断句必须有正确的真假,没有非0即真
      • while后面的()可以省略
      var a = 0
      while a < 10 {
          a++
      }
    • do while循环

      • 使用repeat关键字来代替了do
      let b = 0
      repeat {
          print(b)
          b++
      } while b < 20

    字符串的介绍

    • 字符串在任何的开发中使用都是非常频繁的
    • OC和Swift中字符串的区别
      • 在OC中字符串类型时NSString,在Swift中字符串类型是String
      • OC中字符串@”“,Swift中字符串”“
    • 使用 String 的原因String 是一个结构体,性能更高NSString 是一个 OC 对象,性能略差String 支持直接遍历Swift 提供了 String 和 NSString 之间的无缝转换

    字符的定义

    • 定义不可变字符串

      let str = "hello Objective-C"
    • 定义可变字符串

      var str = "hello Swift"

    字符串的使用

    获取字符串的长度
    • 获取字符集合,再获取集合的count属性

      let count = str.characters.count
    遍历字符串
        // 字符串遍历
        var str = "Hello, Swift"
        for c in str.characters {
            print(c)
        }
    字符串拼接
    • 两个字符串的拼接

      let str1 = "Hello"
      let str2 = "World"
      let str3 = str1 + str2
    • 字符串和其他数据类型的拼接

      let name = "why"
      let age = 18
      
      let info = "my name is \(name), age is \(age)"
    • 字符串的格式化

      • 比如时间:03:04
      let min = 3
      let second = 4
      
      let time = String(format: "%02d:%02d", arguments: [min, second])
    字符串的截取
    • Swift中提供了特殊的截取方式
      • 该方式非常麻烦
      • Index创建较为麻烦
    • 简单的方式是将String转成NSString来使用

      • 在标识符后加:as NSString即可
      let myStr = "www.baidu.com"
      var subStr = (myStr as NSString).substringFromIndex(4)
      subStr = (myStr as NSString).substringToIndex(3)
      subStr = (myStr as NSString).substringWithRange(NSRange(location: 4, length: 5))
    • swift截取方式

      // 1.定义字符串
      let str = "www.baidu.com"
      
      // 2.截取开始位置
      let fromIndex = str.startIndex.advancedBy(3)
      let header = str.substringFromIndex(fromIndex)
      
      // 3.截取结束位置
      let toIndex = str.endIndex.advancedBy(-3)
      let footer = str.substringToIndex(toIndex)
      
      // 4.截取中间的字符串
      let range = Range(start: str.startIndex.advancedBy(4), end: str.endIndex.advancedBy(-4))
      let middle = str.substringWithRange(range)

    数组

    数组的介绍

    • 数组(Array)是一串有序的由相同类型元素构成的集合
    • 数组中的集合元素是有序的,可以重复出现
    • Swift中的数组
      • swift数组类型是Array,是一个泛型集合

    数组的初始化

    • 数组分成:可变数组和不可变数组

      • 使用let修饰的数组是不可变数组
      • 使用var修饰的数组是可变数组
      // 定义一个可变数组,必须初始化才能使用
      var array1 : [String] = [String]()
      
      // 定义一个不可变数组
      let array2 : [NSObject] = ["why", 18]
    • 在声明一个Array类型的时候可以使用下列的语句之一

      var stuArray1:Array<String>
      var stuArray2: [String]
    • 声明的数组需要进行初始化才能使用,数组类型往往是在声明的同时进行初始化的

      // 定义时直接初始化
      var array = ["why", "lnj", "lmj"]
      // 先定义,后初始化
      var array : Array<String>
      array = ["why", "lnj", "lmj"]

    对数组的基本操作

        // 添加数据
        array.append("yz")
    
        // 删除元素
        array.removeFirst()
    
        // 修改元素
        array[0] = "why"
    
        // 取值
        array[1]

    数组的遍历

        // 遍历数组
        for i in 0..<array.count {
            print(array[i])
        }
    
        // forin方式
        for item in array {
            print(item)
        }
    
        // 设置遍历的区间
        for item in array[0..<2] {
            print(item)
        }
    
        // 遍历数组的同时获取下标值
        let names = ["why", "yz", "lnj", "lmj"]
        for (index, name) in names.enumerate() {
            print(index)
            print(name)
        }

    数组的合并

        // 数组合并
        // 注意:只有相同类型的数组才能合并
        var array = ["why", "lmj","lnj"]
        var array1 = ["yz", "wsz"]
        var array2 = array + array1;
    
        // 不建议一个数组中存放多种类型的数据
        var array3 = [2, 3, "why"]
        var array4 = ["yz", 23]
        array3 + array4

    字典

    字典的介绍

    • 字典允许按照某个键来访问元素
    • 字典是由两部分集合构成的,一个是键(key)集合,一个是值(value)集合
    • 键集合是不能有重复元素的,而值集合是可以重复的,键和值是成对出现的
    • Swift中的字典
      • Swift字典类型是Dictionary,也是一个泛型集合

    字典的初始化

    • Swift中的可变和不可变字典

      • 使用let修饰的数组是不可变字典
      • 使用var修饰的数组是可变字典
      // 定义一个可变字典
      var dict1 : [String : NSObject] = [String : NSObject]()
      
      // 定义一个不可变字典
      let dict2 = ["name" : "why", "age" : 18]
    • 在声明一个Dictionary类型的时候可以使用下面的语句之一

      var dict1: Dictionary<Int, String>
      var dict2: [Int: String]
    • 声明的字典需要进行初始化才能使用,字典类型往往是在声明的同时进行初始化的

      // 定时字典的同时,进行初始化
      var dict = ["name" : "why", "age" : 18]

    // swift中任意对象,通常不使用NSObject,使用AnyObject
    var dict : Dictionary

    字典的基本操作

        // 添加数据
        dict["height"] = 1.88
        dict["weight"] = 70.0
        dict
    
        // 删除字段
        dict.removeValueForKey("height")
        dict
    
        // 修改字典
        dict["name"] = "lmj"
        dict.updateValue("lmj", forKey: "name")
        dict
    
        // 查询字典
        dict["name"]

    字典的遍历

        // 遍历字典中所有的值
        for value in dict.values {
            print(value)
        }
        // 遍历字典中所有的键
        for key in dict.keys {
            print(key)
        }
    
        // 遍历所有的键值对
        for (key, value) in dict {
            print(key)
            print(value)
        }

    字典的合并

        // 字典的合并
        var dict1 = ["name" : "yz", "age" : 20]
        var dict2 = ["height" : 1.87, "phoneNum" : "+86 110"]
        // 字典不可以相加合并
        for (key, value) in dict1 {
            dict2[key] = value
        }

    元组

    元组的介绍

    • 元组是Swift中特有的,OC中并没有相关类型
    • 它是什么呢?
      • 它是一种数据结构,在数学中应用广泛
      • 类似于数组或者字典
      • 可以用于定义一组数据
      • 组成元组类型的数据可以称为“元素”

    元组的定义

    • 元组的常见写法

      // 使用元组描述一个人的信息
      ("1001", "张三", 30, 90)
      // 给元素加上元素名称,之后可以通过元素名称访问元素
      (id:"1001", name:"张三", english_score:30, chinese_score:90)

    元组的简单使用

    • 用元组来描述一个HTTP的错误信息

      // 元组:HTTP错误
      // let array = [404, "Not Found"]
      // 写法一:
      let error = (404, "Not Found")
      print(error.0)
      print(error.1)
      
      // 写法二:
      let error = (errorCode : 404, errorInfo : "Not Found")
      print(error.errorCode)
      print(error.errorInfo)
      
      // 写法三:
      let (errorCode, errorIno) = (404, "Not Found")
      print(errorCode)
      print(errorIno)

    可选类型

    可选类型的介绍

    • 注意:
      • 可选类型时swift中较理解的一个知识点
      • 暂时先了解,多利用Xcode的提示来使用
      • 随着学习的深入,慢慢理解其中的原理和好处
    • 概念:
      • 在OC开发中,如果一个变量暂停不使用,可以赋值为0(基本属性类型)或者赋值为空(对象类型)
      • 在swift开发中,nil也是一个特殊的类型.因为和真实的类型不匹配是不能赋值的(swift是强类型语言)
      • 但是开发中赋值nil,在所难免.因此推出了可选类型
    • 可选类型的取值:
      • 空值
      • 有值

    定义可选类型

    • 定义一个可选类型有两种写法

      • 最基本的写法
      • 语法糖(常用)
      // 错误写法
      // let string : String = nil
      // 正确写法:
      // 注意:name的类型是一个可选类型,但是该可选类型中可以存放字符串.
      // 写法一:定义可选类型
      let name : Optional<String> = nil
      
      // 写法二:定义可选类型,语法糖(常用)
      let name : String? = nil

    可选类型的使用

        // 演练一:给可选类型赋值
        // 定义可选类型
        var string : Optional<String> = nil
    
        // 给可选类型赋值
        // 错误写法:因此该可选类型中只能存放字符串
        string = 123
        // 正确写法:
        string = "Hello world"
    
        // 打印结果
        print(string)
        // 结果:Optional("Hello world")\n
        // 因为打印出来的是可选类型,所有会带Optional
    
    
        // 演练二:取出可选类型的值
        // 取出可选类型的真实值(解包)
        print(string!)
        // 结果:Hello world\n
    
        // 注意:如果可选类型为nil,强制取出其中的值(解包),会出错
        string = nil
        print(string!) // 报错
    
        // 正确写法:
        if string != nil {
            print(string!)
        }
    
        // 简单写法:为了让在if语句中可以方便使用string
        // 可选绑定
        if let str = string {
            print(str)
        }

    真实应用场景

    • 目的:让代码更加严谨

      // 通过该方法创建的URL,可能有值,也可能没有值.
      // 错误写法:如果返回值是nil时,就不能接收了
      // 如果字符串中有中文,则返回值为nil,因此该方法的返回值就是一个可选类型,而使用一个NSURL类型接收是错误的
      let url : NSURL = NSURL(string: "www.baidu.com")
      
      // 正确写法:使用可选类型来接收
      let url : NSURL? = NSURL(string: "www.baidu.com")
      // 该方式利用类型推导
      let url = NSURL(string: "www.baidu.com")
      
      // 通过url来创建request对象:在使用可选类型前要先进行判断是否有值
      // 该语法成为可选绑定(如果url有值就解包赋值给tempURL,并且执行{})
      if let tempUrl = url {
          let request = NSURLRequest(URL: tempUrl)
      }

    类型转化

    常见的类型转化符号

    • is : 用于判断一个实例是否是某一种类型
    • as : 将实例转成某一种类型

    例子

        // 1.定义数组
        let array : [AnyObject] = [12, "why", 1.88]
    
        // 2.取出数组中的第一个元素
        let objc = array.first!
    
        // 3.判断第一个元素是否是一个Int类型
        if objc is Int {
            print("是Int类型")
        } else {
            print("非Int类型")
        }
    
        // 4.将objc转成真正的类型来使用
        // 4.1.as? 将AnyObject转成可选类型,通过判断可选类型是否有值,来决定是否转化成功了
        let age = objc as? Int
        print(age) // 结果:Optional(12)
    
        // 4.2.as! 将AnyObject转成具体的类型,但是注意:如果不是该类型,那么程序会崩溃
        let age1 = objc as! Int
        print(age1) // 结果:12

    函数

    函数的介绍

    • 函数相当于OC中的方法
    • 函数的格式如下

      func 函数名(参数列表) -> 返回值类型 {
          代码块
          return 返回值
      }
    • func是关键字,多个参数列表之间可以用逗号(,)分隔,也可以没有参数

    • 使用箭头“->”指向返回值类型
    • 如果函数没有返回值,返回值为Void.并且“-> 返回值类型”部分可以省略

    常见的函数类型

        // 1.没有参数,没用返回值
        func about() -> Void {
            print("iphone6s plus")
        }
        // 调用函数
        about()
    
        // 简单写法
        // 如果没用返回值,Void可以写成()
        func about1() -> () {
            print("iphone6s plus")
        }
        // 如果没有返回值,后面的内容可以都不写
        func about2() {
            print("iphone6s plus")
        }
    
        about2()
    
        // 2.有参数,没用返回值
        func callPhone(phoneNum : String) {
            print("打电话给\(phoneNum)")
        }
        callPhone("+86 110")
    
        // 3.没用参数,有返回值
        func readMessage() -> String {
            return "吃饭了吗?"
        }
        var str = readMessage()
        print(str)
    
        // 4.有参数,有返回值
        func sum(num1 : Int, num2 : Int) -> Int {
            return num1 + num2
        }
        var result = sum(20, num2: 30)
        print(result)
    
        // 5.有多个返回值的函数
        let nums = [1, 3, 4, 8, 22, 23]
        func getNumCount(nums : [Int]) -> (oddCount : Int, evenCount : Int) {
            var oddCount = 0
            var evenCount = 0
            for num in nums {
                if num % 2 == 0 {
                    oddCount++
                } else {
                    evenCount++
                }
            }
            return (oddCount, evenCount)
        }
    
        let result = getNumCount(nums)
        result.oddCount
        result.evenCount

    函数的使用注意

    • 注意一: 外部参数和内部参数

      • 在函数内部可以看到的参数,就是内部参数
      • 在函数外面可以看到的参数,就是外部参数
      • 默认情况下,从第二个参数开始,参数名称既是内部参数也是外部参数
      • 如果第一个参数也想要有外部参数,可以设置标签:在变量名前加标签即可
      • 如果不想要外部参数,可以在参数名称前加_
      // num1和a是外部参数的名称
      func ride(num1 num1 : Int, a num2 : Int, b num3 : Int) -> Int {
          return num1 * num2 * num3
      }
      var result1 = ride(num1: 20, a: 4, b: 5)
      
      // 方法的重载:方法名称相同,但是参数不同,可以称之为方法的重载(了解)
      func ride(num1: Int, _ num2 :Int) -> Int {
          return num1 * num2
      }
      
      var result2 = ride(20, 20)
    • 注意二: 默认参数

      • 某些情况,如果没有传入具体的参数,可以使用默认参数
      func makecoffee(type :String = "卡布奇诺") -> String {
          return "制作一杯\(type)咖啡。"
      }
      
      let coffee1 = makecoffee("拿铁")
      let coffee2 = makecoffee()
    • 注意三: 可变参数

      • swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数
      • 它们必须具有相同的类型
      • 我们可以通过在参数类型名后面加入(…)的方式来指示这是可变参数
      func sum(numbers:Double...) -> Double {
          var total: Double = 0
          for number in numbers {
              total += number
          }
          return total
      }
      
      sum(100.0, 20, 30)
      sum(30, 80)
    • 注意四: 引用类型(指针的传递)

      • 默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址
      • 必须是变量,因为需要在内部改变其值
      • Swift提供的inout关键字就可以实现
      • 对比下列两个函数
      // 函数一:值传递
      func swap(var a : Int, var b : Int) {
          let temp = a;
          a = b;
          b = temp
      
          print("a:\(a), b:\(b)")
      }
      
      var a = 10
      var b = 20
      swap(a, b: b)
      print("a:\(a), b:\(b)")
      
      // 函数二:指针的传递
      func swap1(inout a : Int, inout b : Int) {
          let temp = a
          a = b
          b = temp
      
          print("a:\(a), b:\(b)")
      }
      
      swap1(&a, b: &b)
      print("a:\(a), b:\(b)")
    • 函数的嵌套使用

      • swift中函数可以嵌套使用
      • 即函数中包含函数,但是不推荐该写法
      // 函数的嵌套
      let value = 55
      func test() {
          func demo() {
              print("demo \(value)")
          }
      
          print("test")
          demo()
      }
      
      demo() // 错误
      test() // 执行函数会先打印'test',再打印'demo'

    函数的类型

    • 函数类型的概念

      • 每个函数都有属于自己的类型,由函数的参数类型和返回类型组成
      • 这个例子中定义了两个简单的数学函数:addTwoInts 和 multiplyTwoInts
      • 这两个函数都传入两个 Int 类型, 返回一个合适的Int值
      • 这两个函数的类型是 (Int, Int) -> Int
        // 定义两个函数
        func addTwoInts(a : Int, b : Int) -> Int {
            return a + b
        }
      
        func multiplyTwoInt(a : Int, b : Int) -> Int {
            return a * b
        }
    • 抽取两个函数的类型,并且使用

      // 定义函数的类型
      var mathFunction : (Int, Int) -> Int = addTwoInts
      
      // 使用函数的名称
      mathFunction(10, 20)
      
      // 给函数的标识符赋值其他值
      mathFunction = multiplyTwoInt
      
      // 使用函数的名称
      mathFunction(10, 20)
    • 函数作为方法的参数

      // 3.将函数的类型作为方法的参数
      func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
          print(calculateMethod(a, b))
      }
      
      printResult(10, b: 20, calculateMethod: addTwoInts)
      printResult(10, b: 20, calculateMethod: multiplyTwoInt)
    • 函数作为方法的返回值

      // 1.定义两个函数
      func stepForward(num : Int) -> Int {
          return num + 1
      }
      
      func stepBackward(num : Int) -> Int {
          return num - 1
      }
      
      // 2.定义一个变量,希望该变量经过计算得到0
      var num = -4
      
      // 3.定义获取哪一个函数
      func getOprationMethod(num : Int) -> (Int) -> Int {
          return num <= 0 ? stepForward : stepBackward
      }
      
      // 4.for玄幻进行操作
      while num != 0 {
          let oprationMethod = getOprationMethod(num)
          num = oprationMethod(num)
          print(num)
      }

    枚举类型

    枚举类型的介绍

    • 概念介绍

      • 枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值。
      • 在 C/OC 语言中枚举指定相关名称为一组整型值
      • Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值.也可以提供一个值是字符串,一个字符,或是一个整型值或浮点值
    • 枚举类型的语法

      • 使用enum关键词并且把它们的整个定义放在一对大括号内
          enum SomeEnumeration {
          // enumeration definition goes here
          }

    枚举类型的定义

    • 以下是指南针四个方向的一个例子

      • case关键词表明新的一行成员值将被定义
      • 不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值
      • 在上面的CompassPoints例子中,North,South,East和West不是隐式的等于0,1,2和3
      enum CompassPoint {
        case North
        case South
        case East
        case West
      }
    • 定义方式二:多个成员值可以出现在同一行上

      enum Planet {
        case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
      }

    给枚举类型赋值

    • 枚举类型赋值可以是字符串/字符/整型/浮点型

      • 注意如果有给枚举类型赋值,则必须在枚举类型后面明确说明具体的类型
      // 1.枚举类型的赋值
      enum CompassPoint : Int {
        case North = 1
        case South = 2
        case East = 3
        case West = 4
      }
      
      enum Planet {
        case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
      }
      
      // 2.枚举类型的使用
      let p = Planet(rawValue: 3)
      
      if let p = p {
          switch p {
          case .Mercury:
              print("Mercury")
          case .Venus:
              print("Venus")
          case .Earth:
              print("Mercury")
          case .Mars:
              print("Mars")
          case .Jupiter:
              print("Jupiter")
          case .Saturn:
              print("Saturn")
          case .Uranus:
              print("Uranus")
          case .Neptune:
              print("Neptune")
          }
      }

    结构体

    结构体的介绍

    • 概念介绍
      • 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合
      • 结构体(struct)指的是一种数据结构
      • 结构体是值类型,在方法中传递时是值传递
    • 结构的定义格式

      struct 结构体名称 {
          // 属性和方法
      }

    为什么需要结构体?

    • 先来看一个例子
      • 我们要计算平面坐标里某个点距点Center的距离是否小于200
      • 算起来很简单,勾股定理就搞定了:
      • 其中sqrt(n)用来计算n的平方根
      • pow(x, n)用来计算x的n次方
          let centerX : Double = 100
          let centerY : Double = 100
    
          func inRange(x : Double, y : Double) -> Bool {
              let disX = x - centerX
              let disY = y - centerX
    
              let dis = sqrt(pow(disX, 2) + pow(disY, 2))
    
              return dis < 200
          }
    
          let x : Double = 100
          let y : Double = 1000
    
          inRange(x, y: y)
    • 问题

      • 但是这样有一个不足,当我们需要比较很多个点和Center的距离的时候,这些数字并不能明确告诉我们它们代表的位置的意义,甚至我们都无法知道它们代表一个数字。
      • 如果我们可以像这样来比较位置:
      • 相比数字,它们看上去就会直观的多
      • 而这,就是我们需要自定义struct类型最直接的原因
        inRange(location1)
        inRange(myHome)
    • 使用结构进行改进

      // 初始化结构体
      struct Location {
          var x : Double
          var y : Double
      }
      
      // 创建结构体
      let location = Location(x: 90, y: 90)
      
      // 优化刚才的方法
      func inRange(location : Location) -> Bool {
          let disX = location.x - centerX
          let disY = location.y - centerY
      
          let dis = sqrt(pow(disX, 2) + pow(disY, 2))
      
          return dis < 200
      }
      
      inRange(location)

    结构体的增强

    • 扩充构造函数

      • 默认情况下创建Location时使用Location(x: x值, y: y值)
      • 但是为了让我们在使用结构体时更加的灵活,swift还可以对构造函数进行扩充
      • 扩充的注意点
      • 在扩充的构造函数中必须保证成员变量是有值的
      • 扩充的构造函数会覆盖原有的构造函数
        struct Location {
            var x : Double
            var y : Double
      
            init(x : Double, y : Double) {
                self.x = x
                self.y = y
            }
      
            init(xyString : String) {
                let strs = xyString.componentsSeparatedByString(",")
                x = Double(strs.first!)!
                y = Double(strs.last!)!
            }
        }
      
        let location = Location(x: 100, y: 100)
        let location1 = Location(xyString: "100,100")
    • 为结构体扩充方法

      • 为了让结构体使用更加灵活,swift的结构体中可以扩充方法
      • 例子:为了Location结构体扩充两个方法
      • 向水平方向移动的方法
      • 向垂直方向移动的方法
        struct Location {
            var x : Double
            var y : Double
      
            init(x : Double, y : Double) {
                self.x = x
                self.y = y
            }
      
            init(xyString : String) {
                let strs = xyString.componentsSeparatedByString(",")
                x = Double(strs.first!)!
                y = Double(strs.last!)!
            }
      
            mutating func moveH(x : Double) {
                self.x += x
            }
      
            mutating func moveV(y : Double) {
                self.y += y
            }
        }
    • 注意:

      • 如果我们使用的Location不是自己定义的,但是我们仍旧希望在自己的项目里扩展Location的操作
      • Swift也能帮我们达成,这个机制,叫做extension
      extension Location {
          mutating func moveH(x : Double) {
              self.x += x
          }
      
          mutating func moveV(y : Double) {
              self.y += y
          }
      }

    Swift中类的使用

    主要内容

    • 类的介绍和定义
    • 类的属性
    • 类的构造函数

    一. 类的介绍和定义

    • Swift也是一门面向对象开发的语言
    • 面向对象的基础是类,类产生了对象
    • 在Swift中如何定义类呢?

      • class是Swift中的关键字,用于定义类
      class 类名 : SuperClass {
          // 定义属性和方法
      }
    • 注意:

      • 定义的类,可以没有父类.那么该类是rootClass
      • 通常情况下,定义类时.继承自NSObject(非OC的NSObject)

    二. 如何定义类的属性

    类的属性介绍
    • Swift中类的属性有多种
      • 存储属性:存储实例的常量和变量
      • 计算属性:通过某种方式计算出来的属性
      • 类属性:与整个类自身相关的属性
    存储属性
    • 存储属性是最简单的属性,它作为类实例的一部分,用于存储常量和变量
    • 可以给存储属性提供一个默认值,也可以在初始化方法中对其进行初始化
    • 下面是存储属性的写法

      • age和name都是存储属性,用来记录该学生的年龄和姓名
      • chineseScore和mathScore也是存储属性,用来记录该学生的语文分数和数学分数
      class Student : NSObject {
          // 定义属性
          // 存储属性
          var age : Int = 0
          var name : String?
      
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
      }
      
      // 创建学生对象
      let stu = Student()
      
      // 给存储属性赋值
      stu.age = 10
      stu.name = "why"
      
      stu.chineseScore = 89.0
      stu.mathScore = 98.0
    计算属性
    • 计算属性并不存储实际的值,而是提供一个getter和一个可选的setter来间接获取和设置其它属性
    • 计算属性一般只提供getter方法
    • 如果只提供getter,而不提供setter,则该计算属性为只读属性,并且可以省略get{}
    • 下面是计算属性的写法

      • averageScore是计算属性,通过chineseScore和mathScore计算而来的属性
      • 在setter方法中有一个newValue变量,是系统指定分配的
      class Student : NSObject {
          // 定义属性
          // 存储属性
          var age : Int = 0
          var name : String?
      
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
      
          // 计算属性
          var averageScore : Double {
              get {
                  return (chineseScore + mathScore) / 2
              }
      
              // 没有意义,因为之后获取值时依然是计算得到的
              // newValue是系统分配的变量名,内部存储着新值
              set {
                  self.averageScore = newValue
              }
          }
      }
      
      // 获取计算属性的值
      print(stu.averageScore)
    类属性
    • 类属性是与类相关联的,而不是与类的实例相关联
    • 所有的类和实例都共有一份类属性.因此在某一处修改之后,该类属性就会被修改
    • 类属性的设置和修改,需要通过类来完成
    • 下面是类属性的写法

      • 类属性使用static来修饰
      • courseCount是类属性,用来记录学生有多少门课程
      class Student : NSObject {
          // 定义属性
          // 存储属性
          var age : Int = 0
          var name : String?
      
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
      
          // 计算属性
          var averageScore : Double {
              get {
                  return (chineseScore + mathScore) / 2
              }
      
              // 没有意义.newValue是系统分配的变量名,内部存储着新值
              set {
                  self.averageScore = newValue
              }
          }
      
          // 类属性
          static var corseCount : Int = 0
      }
      
      // 设置类属性的值
      Student.corseCount = 3
      // 取出类属性的值
      print(Student.corseCount)
    监听属性的改变
    • 在OC中我们可以重写set方法来监听属性的改变
    • Swift中可以通过属性观察者来监听和响应属性值的变化
    • 通常是监听存储属性和类属性的改变.(对于计算属性,我们不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化)
    • 我们通过设置以下观察方法来定义观察者
      • willSet:在属性值被存储之前设置。此时新属性值作为一个常量参数被传入。该参数名默认为newValue,我们可以自己定义该参数名
      • didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValue
      • willSet与didSet只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法
    • 监听的方式如下:

      • 监听age和name的变化
      class Person : NSObject {
          var name : String? {
              // 可以给newValue自定义名称
              willSet (new){ // 属性即将改变,还未改变时会调用的方法
                  // 在该方法中有一个默认的系统属性newValue,用于存储新值
                  print(name)
                  print(new)
              }
              // 可以给oldValue自定义名称
              didSet (old) { // 属性值已经改变了,会调用的方法
                  // 在该方法中有一个默认的系统属性oldValue,用于存储旧值
                  print(name)
                  print(old)
              }
          }
          var age : Int = 0
          var height : Double = 0.0
      }
      
      let p : Person = Person()
      
      // 在赋值时,监听该属性的改变
      // 在OC中是通过重写set方法
      // 在swift中,可以给属性添加监听器
      p.name = "why"
      
      //p.name = "yz"

    类的构造函数

    构造函数的介绍

    • 构造函数类似于OC中的初始化方法:init方法
    • 默认情况下载创建一个类时,必然会调用一个构造函数
    • 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。
    • 如果是继承自NSObject,可以对父类的构造函数进行重写

    构造函数的基本使用

    构造函数的基本使用
    • 类的属性必须有值
    • 如果不是在定义时初始化值,可以在构造函数中赋值

      class Person: NSObject {
          var name : String
          var age : Int
      
          // 重写了NSObject(父类)的构造方法
          override init() {
              name = ""
              age = 0
          }
      }
      
      // 创建一个Person对象
      let p = Person()
    初始化时给属性赋值
    • 很多时候,我们在创建一个对象时就会给属性赋值
    • 可以自定义构造函数
    • 注意:如果自定义了构造函数,会覆盖init()方法.即不在有默认的构造函数

      class Person: NSObject {
          var name : String
          var age : Int
      
          // 自定义构造函数,会覆盖init()函数
          init(name : String, age : Int) {
              self.name = name
              self.age = age
          }
      }
      
      // 创建一个Person对象
      let p = Person(name: "why", age: 18)
    字典转模型(初始化时传入字典)
    • 真实创建对象时,更多的是将字典转成模型
    • 注意:

      • 去字典中取出的是NSObject,任意类型.
      • 可以通过as!转成需要的类型,再赋值(不可以直接赋值)
      class Person: NSObject {
          var name : String
          var age : Int
      
          // 自定义构造函数,会覆盖init()函数
          init(dict : [String : NSObject]) {
              name = dict["name"] as! String
              age = dict["age"] as! Int
          }
      }
      
      // 创建一个Person对象
      let dict = ["name" : "why", "age" : 18]
      let p = Person(dict: dict)
    字典转模型(利用KVC转化)
    • 利用KVC字典转模型会更加方便
    • 注意:

      • KVC并不能保证会给所有的属性赋值
      • 因此属性需要有默认值
      • 基本数据类型默认值设置为0
      • 对象或者结构体类型定义为可选类型即可(可选类型没有赋值前为nil)
        class Person: NSObject {
            // 结构体或者类的类型,必须是可选类型.因为不能保证一定会赋值
            var name : String?
      
            // 基本数据类型不能是可选类型,否则KVC无法转化
            var age : Int = 0
      
            // 自定义构造函数,会覆盖init()函数
            init(dict : [String : NSObject]) {
                // 必须先初始化对象
                super.init()
      
                // 调用对象的KVC方法字典转模型
                setValuesForKeysWithDictionary(dict)
            }
        }
      
        // 创建一个Person对象
        let dict = ["name" : "why", "age" : 18]
        let p = Person(dict: dict)

    类的析构函数

    析构函数

    • Swift 会自动释放不再需要的实例以释放资源
      • Swift 通过自动引用计数(ARC)处理实例的内存管理
      • 当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
      • 通常在析构函数中释放一些资源(如移除通知等操作)
    • 析构函数的写法

      deinit {
          // 执行析构过程
      }

    示例练习

        class Person {
            var name : String
            var age : Int
    
            init(name : String, age : Int) {
                self.name = name
                self.age = age
            }
    
            deinit {
                print("Person-deinit")
            }
        }
    
        var p : Person? = Person(name: "why", age: 18)
        p = nil

    自动引用计数

    工作机制

    • Swift和OC一样,采用自动引用计数来管理内容
      • 当有一个强引用指向某一个动向时,该对象的引用计数会自动+1
      • 当该强引用消失时,引用计数会自动-1
      • 当引用计数为0时,该对象会被销毁

    循环引用

    • 在通常情况下,ARC是会自动帮助我们管理内存的
    • 但是在开发中我们经常会出现循环引用的问题,比如下面的示例

      • Student对Book对象有一个强引用
      • 而Book对Student有一个强引用
      • 在两个对象都指向nil时,依然不会被销毁,就形成了循环引用
      // 1.创建类
      class Student {
          var book : Book?
      
          deinit {
              print("Student -- deinit")
          }
      }
      
      class Book {
          var owner : Student?
      
          deinit {
              print("Book -- deinit")
          }
      }
      
      // 2.创建对象
      var stu : Student? = Student()
      var book : Book? = Book()
      
      // 3.相互引用
      stu?.book = book
      book?.owner = stu
      
      // 4.对象置nil
      stu = nil
      book = nil
    • 解决方案

      • swift提供了两种解决方案
      • weak : 和OC中的__weak一样是一个弱引用.当指向的对象销毁时,会自动将指针指向nil
      • unowned : 和OC中的__unsafe_unretained.当对象销毁时依然指向原来的位置(容易引起野指针)
      // 1.创建类
      class Student {
          weak var book : Book?
          // unowned var book : Book = Book()
      
          deinit {
              print("Student -- deinit")
          }
      }
      
      class Book {
          var owner : Student?
      
          deinit {
              print("Book -- deinit")
          }
      }
      
      // 2.创建对象
      var stu : Student? = Student()
      var book : Book? = Book()
      
      // 3.相互引用
      stu?.book = book!
      book?.owner = stu
      
      // 4.对象置nil
      stu = nil
      book = nil

    可选链

    可选连的概念

    • 它的可选性体现于请求或调用的目标当前可能为空(nil)
      • 如果可选的目标有值,那么调用就会成功;
      • 如果选择的目标为空(nil),则这种调用将返回空(nil)
    • 多次调用被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
    • 可选链的使用
      • 在可选类型后面放一个问号,可以定义一个可选链。
      • 这一点很像在可选值后面放一个叹号来强制拆得其封包内的值
      • 它们的主要的区别在于当可选值为空时可选链即刻失败
      • 然而一般的强制解析将会引发运行时错误。
      • 因为可选链的结果可能为nil,可能有值.因此它的返回值是一个可选类型.
      • 可以通过判断返回是否有值来判断是否调用成功
      • 有值,说明调用成功
      • 为nil,说明调用失败

    可选链的示例

    • 从可选链中取值

      • 示例描述: 人(Person)有一个狗(Dog),狗(Dog)有一个玩具(Toy),玩具有价格(price)
      • 使用代码描述上述信息
      // 1.定义类
      class Person {
          var name : String
          var dog : Dog?
      
          init(name : String) {
              self.name = name
          }
      }
      
      class Dog {
          var color : UIColor
          var toy : Toy?
      
          init(color : UIColor) {
              self.color = color
          }
      
          func runing() {
              print("跑起来")
          }
      }
      
      class Toy {
          var price : Double = 0.0
      }
      
      // 2.创建对象,并且设置对象之间的关系
      // 2.1.创建对象
      let person = Person(name: "小明")
      let dog = Dog(color: UIColor.yellowColor())
      let toy = Toy()
      toy.price = 100.0
      
      // 2.2.设置对象之间的关系
      person.dog = dog
      dog.toy = toy
    • 需求:获取小明的大黄宠物的玩具价格取出的值为可选类型,因为可选链中有一个可选类型为nil,则返回nil因此结果可能有值,可能为nil.因此是一个可选类型

      let price = person.dog?.toy?.price
      print(price) // Optional(100.0)\n
    • 需求:给小明的大黄一个新的玩具

      • 相当于给可选类型赋值
      person.dog?.toy = Toy()
    • 需求:让小明的狗跑起来

      • 如果可选类型有值,则会执行该方法
      • 如果可选类型为nil,则该方法不会执行
      person.dog?.runing()

    协议

    协议的格式

    • 协议的定义方式与类,结构体,枚举的定义都非常相似

      protocol SomeProtocol {
          // 协议方法
      }
    • 遵守协议的格式

      class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
        // 类的内容
        // 实现协议中的方法
      }

    协议的基本使用

    • 定义协议和遵守协议

      // 1.定义协议
      protocol SportProtocol {
          func playBasketball()
          func playFootball()
      }
      
      // 2.遵守协议
      // 注意:默认情况下在swift中所有的协议方法都是必须实现的,如果不实现,则编译器会报错
      class Person : SportProtocol {
          var name : String?
          var age : Int = 0
      
          // 实现协议中的方法
          func playBasketball() {
              print("人在打篮球")
          }
      
          func playFootball() {
              print("人在踢足球")
          }
      }
    • 协议之间的继承

      protocol CrazySportProtocol {
          func jumping()
      }
      
      protocol SportProtocol : CrazySportProtocol {
          func playBasketball()
          func playFootball()
      }

    代理设计模式

    • 协议继承用于代理设计模式

      protocol BuyTicketProtocol {
          func buyTicket()
      }
      
      class Person {
          // 1.定义协议属性
          var delegate : BuyTicketProtocol
      
          // 2.自定义构造函数
          init (delegate : BuyTicketProtocol) {
              self.delegate = delegate
          }
      
          // 3.行为
          func goToBeijing() {
              delegate.buyTicket()
          }
      }
      
      class HuangNiu: BuyTicketProtocol {
          func buyTicket() {
              print("买了一张火车票")
          }
      }
      
      let p = Person(delegate: HuangNiu())
      p.goToBeijing()
      ​```

    协议中方法的可选

        // 1.定义协议
        @objc
        protocol SportProtocol {
            func playBasketball()
    
            optional func playFootball()
        }
    
        // 2.遵守协议
        class Person : SportProtocol {
            var name : String?
            var age : Int = 0
    
            // 实现协议中的方法
            @objc func playBasketball() {
                print("人在打篮球")
            }
        }

    闭包

    闭包的介绍

    • 闭包和OC中的block非常相似
      • OC中的block是匿名的函数
      • Swift中的闭包是一个特殊的函数
      • block和闭包都经常用于回调
    • 注意:闭包和block一样,第一次使用时可能不习惯它的语法,可以先按照使用简单的闭包,随着学习的深入,慢慢掌握其灵活的运用方法.

    闭包的使用

    block的用法回顾
    • 定义网络请求的类

      @interface HttpTool : NSObject
      - (void)loadRequest:(void (^)())callBackBlock;
      @end
      
      @implementation HttpTool
      - (void)loadRequest:(void (^)())callBackBlock
      {
          dispatch_async(dispatch_get_global_queue(0, 0), ^{
              NSLog(@"加载网络数据:%@", [NSThread currentThread]);
      
              dispatch_async(dispatch_get_main_queue(), ^{
                  callBackBlock();
              });
          });
      }
      @end
    • 进行网络请求,请求到数据后利用block进行回调

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
      {
          [self.httpTool loadRequest:^{
              NSLog(@"主线程中,将数据回调.%@", [NSThread currentThread]);
          }];
      }
    • block写法总结:

      block的写法:
      类型:
      返回值(^block的名称)(block的参数)
      
      值:
      ^(参数列表) {
        // 执行的代码
      };
    使用闭包代替block
    • 定义网络请求的类

      class HttpTool: NSObject {
      
          func loadRequest(callBack : ()->()){
              dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                  print("加载数据", [NSThread.currentThread()])
      
                   dispatch_async(dispatch_get_main_queue(), { () -> Void in
                      callBack()
                   })
              }
          }
      }
    • 进行网络请求,请求到数据后利用闭包进行回调

        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            // 网络请求
            httpTool.loadRequest ({ () -> () in
                print("回到主线程", NSThread.currentThread());
            })
        }
    • 闭包写法总结:

      闭包的写法:
          类型:(形参列表)->(返回值)
          技巧:初学者定义闭包类型,直接写()->().再填充参数和返回值
      
          值:
          {
              (形参) -> 返回值类型 in
              // 执行代码
          }
    闭包的简写
    • 如果闭包没有参数,没有返回值.in和in之前的内容可以省略

        httpTool.loadRequest({
            print("回到主线程", NSThread.currentThread());
        })
    • 尾随闭包写法:

      • 如果闭包是函数的最后一个参数,则可以将闭包写在()后面
      • 如果函数只有一个参数,并且这个参数是闭包,那么()可以不写
        httpTool.loadRequest() {
            print("回到主线程", NSThread.currentThread());
        }
      
        // 开发中建议该写法
        httpTool.loadRequest {
            print("回到主线程", NSThread.currentThread());
        }

    闭包的循环引用

    • 如果在HttpTool中有对闭包进行强引用,则会形成循环引用
    • 补充:在Swift中检测一个对象是否销毁,可以实现对象的deinit函数

        // 析构函数(相当于OC中dealloc方法)
        deinit {
            print("ViewController----deinit")
        }
    • 循环引用的(实现)

      • 该实现是为了产生循环引用,而产生的循环引用
      class HttpTool: NSObject {
      
          // 定义属性,来强引用传入的闭包
          var callBack : (()->())?
      
          func loadRequest(callBack : ()->()){
              dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                  print("加载数据", [NSThread.currentThread()])
      
                   dispatch_async(dispatch_get_main_queue(), { () -> Void in
                      callBack()
                   })
              }
      
              self.callBack = callBack
          }
      }
    • swift中解决循环引用的方式

    • 方案一:

      • 使用weak,对当前控制器使用弱引用
      • 但是因为self可能有值也可能没有值,因此weakSelf是一个可选类型,在真正使用时可以对其强制解包(该处强制解包没有问题,因为控制器一定存在,否则无法调用所在函数)
        // 解决方案一:
        weak var weakSelf = self
        httpTool.loadData {
            print("加载数据完成,更新界面:", NSThread.currentThread())
            weakSelf!.view.backgroundColor = UIColor.redColor()
        }
    • 方案二:

      • 和方案一类型,只是书写方式更加简单
      • 可以写在闭包中,并且在闭包中用到的self都是弱引用
        httpTool.loadData {[weak self] () -> () in
            print("加载数据完成,更新界面:", NSThread.currentThread())
            self!.view.backgroundColor = UIColor.redColor()
        }
    • 方案三:(常用)

      • 使用关键字unowned
      • 从行为上来说 unowned 更像OC中的 unsafe_unretained
      • unowned 表示:即使它原来引用的对象被释放了,仍然会保持对被已经释放了的对象的一个 “无效的” 引用,它不能是 Optional 值,也不会被指向 nil
      httpTool.loadData {[unowned self] () -> () in
              print("加载数据完成,更新界面:", NSThread.currentThread())
              self.view.backgroundColor = UIColor.redColor()
          }

    懒加载

    懒加载的介绍

    • swift中也有懒加载的方式
      • (苹果的设计思想:希望所有的对象在使用时才真正加载到内存中)
    • 和OC不同的是swift有专门的关键字来实现懒加载
    • lazy关键字可以用于定义某一个属性懒加载

    懒加载的使用

    • 格式

      lazy var 变量: 类型 = { 创建变量代码 }()
    • 懒加载的使用

        // 懒加载的本质是,在第一次使用的时候执行闭包,将闭包的返回值赋值给属性
        // lazy的作用是只会赋值一次
        lazy var array : [String] = {
            () -> [String] in
            return ["why", "lmj", "lnj"]
        }()

    常见注释

    单行注释

    • Swift 中的注释与C 语言的注释非常相似。
    • 单行注释以双正斜杠(//)作为起始标记

      // 注释内容

    多行注释

    • 其起始标记为单个正斜杠后跟随一个星号(/*)
    • 终止标记为一个星号后跟随单个正斜杠(*/)

      /* 这是一个,
      多行注释 */
    • 和与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中

      /* 这是第一个多行注释的开头
      /* 这是第二个被嵌套的多行注释 */
      这是第一个多行注释的结尾 */

    文档注释

    • Swift中添加文档注释较为简单
    • 使用(///)可以为方法或者属性添加文档注释

      /// 打电话给某人
      func callPhone(phoneNum : String) {
          print("打电话给\(phoneNum)")
      }

    分组注释

    • swift中不可以再使用 #pragma mark -
    • 如果打算对代码进行分组可以使用 // MARK:-方式

      // MARK:-

    访问权限

    swift中的访问权限

    • Swift 中的访问控制模型基于模块和源文件这两个概念

      - internal : 在本模块中都可以进行访问
      - private : 在当前源文件中可以访问
      - public : 在其他模块中可以访问

    异常处理

    异常的介绍

    • 只要我们在编程,就一定要面对错误处理的问题。
    • Swift在设计的时候就尽可能让我们明确感知错误,明确处理错误
      • 比如:只有使用Optional才能处理空值;
    • 如何描述一个错误?
      • 在Swift里,任何一个遵从ErrorType protocol的类型,都可以用于描述错误。
      • ErrorType是一个空的protocol,它唯一的功能,就是告诉Swift编译器,某个类型用来表示一个错误。
      • 通常,我们使用一个enum来定义各种错误的可能性

    异常的示例

    • 假如我们想要读取一个文件中的内容,按照OC的逻辑我们可以这样来模拟

      • 当我们调用方法获取结果为nil时,你并不能确定到底参数了什么错误得到了nil
      func readFileContent(filePath : String) -> String? {
          // 1.filePath为""
          if filePath == "" {
              return nil
          }
      
          // 2.filepath有值,但是没有对应的文件
          if filePath != "/User/Desktop/123.plist" {
              return nil
          }
      
          // 3.取出其中的内容
          return "123"
      }
      
      readFileContent("abc")
    • 使用异常对上述方法进行改进

      // 1.定义异常
      enum FileReadError : ErrorType {
          case FileISNull
          case FileNotFound
      }
      
      // 2.改进方法,让方法抛出异常
      func readFileContent(filePath : String) throws -> String {
          // 1.filePath为""
          if filePath == "" {
      
              throw FileReadError.FileISNull
          }
      
          // 2.filepath有值,但是没有对应的文件
          if filePath != "/User/Desktop/123.plist" {
      
              throw FileReadError.FileISNull
          }
      
          // 3.取出其中的内容
          return "123"
      }
    • 处理异常有三种方式

      // 3.异常的处理三种方式
      // 3.1.try方式,需要手动处理异常
      do {
          let result = try readFileContent("abc")
      } catch {
          print(error)
      }
      
      // 3.2.try?方式,不处理异常,如果出现了异常,则返回一个nil.没有异常,则返回对应的值
      // 最终返回结果为一个可选类型
      let result = try? readFileContent("abc")
      
      // 3.3.try!方法,告诉系统该方法没有异常.
      // 注意:如果出现了异常,则程序会崩溃
      try! readFileContent("abc")

    Swift和OC相互调⽤用

    Swift调⽤用OC

    1. 创建桥接⽂文件—> .h
    2. 在桥接⽂文件中导⼊入头⽂文件
    3. 配置桥接⽂文件: 项目->buildSettings —> bridging —> 配置

    OC调⽤用Swift

    • 项⽬目名字不能随便起
    • Swift中的类/属性/⽅方法必须使⽤用public修饰
    • 导⼊项目名称-Swift.h

    简书地址:http://www.jianshu.com/users/227bbeb09f91/latest_articles

    展开全文
  • swift笔记

    2020-02-04 10:11:11
    Swift5.1 https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html 1、swift没有隐式转化 Swift中没有隐式转化,不会将整形自动转成浮点型 let m = 32 let n = 3.14 let result = m + n 错误写法 ...

    Swift5.1 https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html

     

    1、swift没有隐式转化

    Swift中没有隐式转化,不会将整形自动转成浮点型

    let m = 32

    let n = 3.14

     let result = m + n 错误写法

     

    2、省略;  省的是变量后的类型,可以类型推导

     

    3、类型推导 option+鼠标左键

     

    4、

    //1、if的使用

    //if后的()可略

    //判断句不再有非0即真.必须有明确bool值 true/false

     

    5、guard的用法 

    07示例代码写的什么玩意

    guard只能用在函数中,不能像if一样用在函数外;和if用法相似,后边都加条件表达式;和else连用,有guard必会带else;与if不同的是紧跟if的大括号是条件语句成立时,而guard后紧跟的大括号里的大括号是条件语句不成立才走,条件语句成立时则走大括号外边;guard最近的大括号里需要和return,break,continue,throw联用

     

    6、08-switch特出用法

    Switch 

     Switch后的小括号可以省

     默认每个case是包含break,如果想case穿透用fallthrough

     default不能写在最上边,只能写在最下边

     switch 后边的变量不仅可以是整型,也可以是double,字符串,对象类型;

     可以用区间 let range = 0…10   []  可计数   0.01…0.5 不可计数  0..<10 [)  可计数才能遍历;元组做条件;where;case后可以写两个;

    //fallthrough case穿透

    //case后跟多个条件

    //不可以不写default

    //default不能随便放

    //能判断对象,浮点型,字符串等

     

    元组;

    区间;

    //switch

    //一、区间匹配

    //二、元组匹配 即一对数据

    //三、值绑定

    //四、根据条件绑定,where,只有where后的条件表达式为真才赋值并执行case后的语句

     

    7、Swift下划线的作用:_忽略  https://www.jianshu.com/p/fcdfc8306a94

    //1、格式化数字,增加阅读性

    //2、忽略元组的元素值;使用元组时,如果有元素不需要使用,可以使用下划线将元素忽略

    //3、忽略区间值

    //4、忽略外部参数名

      //1》忽略外部参数名

      //方法(类或实例)中,方法的第二个参数名及后续参数名,默认既是内部参数名又是外部参数名,如果不想提供外部参数名,可以在参数前加下划线来忽略外部参数名

      //2》忽略具有默认值的参数的外部参数名

      //当函数的参数有默认值时,swift自动为参数提供与参数名一致的默认外部参数名,因此在进行参数调用时,要提供默认参数名,可以使用下滑线进行忽略默认外部参数名

     

     

    //从for循环开始,用qq课堂的小码哥视频,视频代码地址:/Users/yangyangzi/Desktop/yangyangzi/2019/swift(五部分)

     

    11、while循环:

    While i < 10 {

     

    }

     

    pepeat{

     

    }while i < 10

    repeat {

        

        i += 1

        

        print(i)

        

    } while i < 10

    12、字符串基本使用

    string不是类而是结构体,性能更高,没有存储地址类似于int等,是一个值类型而oc的nsstring为指针类型

    类,存储的字符串在堆里,有寻址操作

     

    let len = str.lengthOfBytes(using: String.Encoding.utf8)

    let ff = str.lengthOfBytes(using: .utf8)//枚举前边的类型是可以省略掉

    1》字符串拼接

    字符串间拼接:+  ; "\()" 

    字符串和其它进行拼接,例如和数字:+  ; "\()"  ; String()

    字符串格式化:String(format:"% “)和oc的%拼接相似

    2》字符串长度

    字符串长度:str.count

    字符串字节长度:str.lengthOfBytes(using: .utf8)

     

     

    13、

    Oc有可变和不可变string

    Swift的字符串可变就是var,不可变就是let;let 和 var相当于有无const

     

    字符串截取:注index类型不是int了变成String.index了

    //替换字符串会改变原字符串

    //截取不会改变原字符串

    //把字符串换成let看那个报错就说明此处会改变原字符串

     

    15、

    1》创建数组时设立某元素的重复次数

    2》count与capacity不是同一概念,数值不一定相同

    3》遍历数组:

         便利索引而遍历元素

         遍历元素:for value in arr {

         数组迭代器::相当于遍历元组组成的数组  for (i,v) in arr.enumerated() {   //arr.enumerated()相当于遍历元组组成的数组

         遍历某区间元素:for v in arr[1...3] {

    4》数组间操作:数组的合并

    数组的加(注意:不存在减法);

    数组的价只能在数组元素类型相同的情况才可相加;

    //一、count与capacity

    arr2.count//数组真正的元素个数

    arr2.capacity//数组的容量,一般情况是2的个数,如果在存元素时超过了容量,容量值capacity就会 * 2(翻倍)

    //二、遍历

    //便利索引而遍历元素

    //遍历元素

    //迭代器;相当于遍历元组组成的数组:[(1,””),(2,””)]

    //区间;arr[1...3]

    //三、数组间操作

    加法://arr + arr3 //不同种类型不能件单加,都变成Any类型即可相加;数组间没有系统减法

     

     

    16、

     

    17、

     

    18、可选类型

    1》oc与swift的nil不同,oc的nil指的空指针特殊的地址;swift的nil就是一个特殊含义的字符,表示没有值

    2》swift中只有可选类型才能赋值为nil,nil是个特殊的字符

    3》可选代表的含义是有可能有值也可能没有值;非可选代表无论在什么时候,在哪里都有值

     

    let num:Optional<Int> = 2//num的是Optional类型的,不是Int

    var num2:Int? = 2//num2是可选类型,此可选类型里包装了Int类型;?说明是可选类型

    var num3:Int! = 2//num3是可选类型,此可选类型里包装了Int类型;!说明是可选类型;

    //加?和!都表示是可选类型,?和!的区别:

    //?表示可选类型:在任何时候有可能有值也有可能没值

    //!标示可选类型:能保证在用到时候不为nil

    /**

     1、何为可选类型?

     一个变量要么有值,要么没值

     2、为何产生可选类型?

     比如:oc中基本数据类型,如果不赋值,都会存在默认值;所以不会产生没有值的情况

     但是:没有值和默认值是完全不同的两概念

     所以在swift中引入了可选类型

     区分了“有值”和“没有值”两种状态

     */

     

    var n:Int = 0//n是一个非可选值

    //n = nil //❌ 因为只有可选类型才能赋值为nil

     n += 1

     

    var m:Int? = 0

    m = nil//m可以为nil,因为m为可选类型

    //print(m) //报错

    //m += 1 optional类型不能直接运算

    m! += 1

     

    var p:Int! = 0

    //p += 1

    p = nil;

    //p += 1;

     

    var q:Int? = 0

    q = 1//可选类型可以直接赋值  ***

    q

     

    // ! :一个含义:强制解包

    // ? 表示可选类型(包装了其它类型) 例如:Int?表示包装了Int类型;String?表示包装了String类型

    //==================  !也可以表示可选类型

    var a:Int! = 12

    print(a)//能正常打印

    a = nil

    print(a)//报错:原因:上边提到的?和!表示可选类型的区别即!表示可选类型必须能保证在用是时候有值,不用再判断ji

    a = 1

    print(a)//这时候又能正常打印了,因为上行赋值了,满足!表示可选类型的要求

     

    //想要使用可选类型的值需要怎么做:1、直接判断+强制解包;2、可选绑定;3、guard守护 4、使用空合运算符?? 

    //1、直接判断+强制解包

    if q != nil {

        var r = q!

        r += 1

    }

     

    //2、可选绑定

    //首先判断 q是否为nil,如果是,那么 if 后面的值为false

    //如果发现q != nil,if后面的值为true,还会把q强制解包,并把解包后的值,赋值给result

    //

    if let r = q {

        r + 1

    }

     

    //3、guard守护(注意guard只能用在函数里边,因此此处要写个函数)

    func test(num:Int?){

        guard let r = num else {

            return

        }

        r + 1

    }

     

    //4、使用空合运算符?? 

    var a:Int?

    var b = a ?? 2 //如果a为nil则赋默认值为2

     

     

    19、类型转换

    /**

     

     is  :用于判断一个实例是否是一种类型

     as  :将实例转化为某一种类型,比如子类转父类(即很合理正常的类型间的转换)

     as? :将某个类型转换为可选类型,通过判断可选类型是否有值,来决定是否转化成功

     as! :将某个类型转化成具体的类型,但是注意:如过不是该类型,会崩溃,所以尽量少用

     

     */

     

    // is ========

    var a = 8.8

    let bool = a is Int

     

    // as ========

    var sdd:String = "123"

    sdd as String

     

    // as!  ========  肯定可以转化成功,转换的结果是非可选类型 不能为nil

    var str:Any = "1d24"

    let ss = str as! String

    ss + "adb"

     

    // as?  ====== 系统尝试进行转换,如果转换失败则为nil,转换结果是可选类型(这里用的是直接判断+强制解包)

    let sadde = str as? String

    if sadde != nil  {

        let cfg = sadde! + "ssd"

        print(cfg)

    }

     

    20、空合运算符

    //空合运算符用法

    //可选类型的变量 ?? “为空时的代替值”

    //代表,如果可选类型为nil,则使用默认值,如果有值,直接解包使用

    //类似于使用三目运算符判断一个变量是否为空

    //空合运算符 ??

    //如果a可选类型的值为nil,那么,取??后面的值,如果,a不等于nil,则a取强制解包后的值

    所以使用可选类型现在变成了五种处理方法如下:

    //使用可选类型的值

    //直接判断+强制解包

    //可选绑定

    //guard守护

    //空合运算符给默认值(处理可选类型为nil的情况,即为nil给个默认值)

     

     

    第二天

    1、函数的基本使用

    //有参,有返回值

    //无参,无返回

    //有参,无返回

    //无参,有返回

    //特例,返回多个参数(即返回一个元组)func five() ->(String,Int,Double{}

     

    //函数 类型方法:类调用的方法,用static修饰的方法,而且不能直接写在外边

    //函数 实例方法:对象调用的方法

     

    2、函数使用注意一

    //一:内部参数;外部参数  函数

    //内部参数:在内部能看到能用的参数

    //外部参数:在外边看到的参数叫外部参数

    //3.0开始,第一个参数开始,既是内部又是外部

    func one(num1:Int,num2:Int)//num1和num2都是内部和外部参数

    func two(_ num1:Int,_ num2:Int)//_ 省略外部参数,num1 num2 是内部参数

    func three(as num1:Int,cc num2:Int)//as cc 外部参数  //num1 num2 内存参数

    //二、默认参数函数

    //必填参数和可填参数混合

    //作用:可以写兼容性很强的接口

    func four(num1:Int,dis:Int = 0,disd:Int = 0) -> Int{} //num1必填 dis为可填 disd为可填

    //三、可变参数函数

    //可变参数类型: 类型...

    //函数内部当数组处理

    //函数外部,直接可以穿多个值

    func five(nums:Int...) -> Int

    func six(_ nums:Int...) -> Int

     

    3、函数使用注意二

    //1、函数内部,默认接收到的参数被赋值给一个常量,在swift3.0中,如果想改变,需要在函数内部,使用var修饰

    func changeNum(a:Int){ var a = a a = 666}// 使用var进行修饰,尽量少用这种方式 ;注:这里是值传递

    //2、将值传递转换为指针传递  inout关键字 //inout 地址

    func oneOne(a: inout Int)   调用传地址 oneOne(a: &b)

    //3、函数嵌套(oc是不能进行函数嵌套的)

    func testA(){

        print("a")

        func testB(){

            print("b")

        }

        testB()

        print("c")

    }

     

    4、函数的类型

    //函数是有类型的

    //函数类型:由参数类型和返回值类型共同组成

    查看函数类型:

    func add(a:Int,b:Int) ->Int {

        return a + b

    }

    let a = add//选中a,option+左键看函数类型为 (Int,Int) -> Int

     

    //1、函数是有类型的,函数作为另一个函数的参数

    func execute(num1:Int,num2:Int,fun:(Int,Int) -> Int){

        fun(num1,num2)

    }

    execute(num1: 9, num2: 3, fun: cheng)

     

    //2、函数是有类型的,函数作为另一个函数的返回值

    func getFun(ff:String) -> (Int,Int)->Int{

        if ff == "add" {

            return add

        }

        return cheng

    }

    //系统如何区分不同的函数

    //1、靠函数名称

    //2、靠外部参数名称

    //3、参数的类型 + 返回值类型 = 函数类型

    //swift支持函数重载,而oc不支持,因为swift可以通过函数类型区分不同函数

     

    5、枚举

    Oc的枚举

    //NS_ENUM表示状态,同一时刻只能取一个

    typedef NS_ENUM(NSUInteger, Direction) {

        DirectionA,

        DirectionB,

        DirectionC,

    };

    //NS_OPTIONS表示选项,同一时刻能取多个

    typedef NS_OPTIONS(NSUInteger, DirectionDirection) {

        DirectionDirectionA = 1 << 0,

        DirectionDirectionB = 1 << 1,

        DirectionDirectionC = 1 << 2,

    };

    //oc的枚举值只能是整型值

    //swift中枚举值可以是字符串、字符、整型值、浮点值;swift枚举类型,默认不表示任何类型,就是一个标识;如果想绑定原始数据,需要在枚举定义时设置,并指定枚举类型

     

    //枚举定义:如下;

    //注意:开头字母小写

    enum Direction{//此枚举值是没有类型的,默认不表示任何类型

        case east

        case sourth

        case west

        case north

    }

     

    enum DirectionTwo: Int{case east = 1  case sourth}//此枚举是Int类型的

    enum DirectionThree: String{case east = "east" case sourth = "sourth"}//此枚举是String类型的

     

    //枚举的元素值rawvalue

    //枚举值-》原始值

    //原始值-》枚举值

    let z = DirectionThree(rawValue: "east")//z是可选类型

     

    //枚举中可以写方法

    //实例方法

    //类型方法

     

    6、结构体

    是由一组相同或不同类型的数据组成的集合,是值类型,值传递

    //实例属性

    //类型属性,必须赋值

    //实例方法,调实例属性和类型属性

    //类型方法,调类型属性

     

    7、结构体构造函数扩充

     扩充构造函数

     init,不能func

     一旦扩充(无论扩充的是几个参数的构造器),结构体存在的系统自带的 逐一构造器,就没了

     

     ****** 一定要注意:在构造体内部,一定要保证,所有非可选属性,必须被初始化 ******

     ****** 且在调用构造器时,一定要保证所有非可选属性,必须有值 ******

    //结构体中系统默认的构造函数,此构造函数默认以所有非可选类型为构造器参数,保证了所有非可选类型在结构体初始化后都有值,即 也叫 逐一构造器

    //逐一构造器:在构造体内部,才能保证,所有非可选属性,必须被初始化

     

    8、结构体函数的扩充

    Swif叫类型扩展

    Oc叫类扩展

    //swift的static类型属性和java类似,在内存中是唯一的,是该类所有实例对象所共享的存储单元

    (自总结)

     mutating func shiliFunc() {//在实例方法中是不能修改实例属性值个,除非在实例函数前加mutating关键字,把self可以改变

            print("在x轴上移动了",x+=1)

     }

    //类型扩展

    //扩充系统结构体进行扩展

    //oc中扩展只能用于类(int等非类的类型就不支持扩展了)

    //swift中的扩展可以用于任何类型,比oc中的扩展范围广

    extension CGPoint{

       mutating func shiliFunc(xD:CGFloat){

            self.x += xD

        }

    }

    var pc = CGPoint(x: 3, y: 4)

    pc.shiliFunc(xD: 3)

     

    9、类的基本使用

    //一、类的非可选属性注意点 =====================

     

    //逐一构造器:保证非可选有值

    /**

     类的非可选属性注意点:

     类和结构体看似一样,但结构体有 逐一构造器,而类没有逐一构造器;但是类和结构体一样在实例对象被创建好之后,保证,里面所有的非可选属性,必须有值,因此可以通过以下方法打到目的

     

     1、把所有的非可选,改成可选

     2、在构造函数里面,给所有非可选属性赋值

     3、给非可选属性,赋值默认值

     */

     

    // 属性:对象属性,类属性

    // 方法:对象方法,类方法

     

    //二、类可以不继承其他类,他本身就是根类 =====================

     

    //如果,想用NSObject里的方法,则需要继承NSObject

     

    10、字典转模型

    kvc

    和oc差不多用模型类

    setValuesForKeys(dic) 

    override func setValue(_ value: Any?, forUndefinedKey key: String) { }//防止不存在的数据导致崩溃

     

    11、类的析构函数,deinit相当于dealloc

    deinit {//相当于oc的delloc方法

            print("对象被释放了")

        }

    var p:Person? = Person()

    p = nil

     

    12、类的属性和方法

    /**

     属性:实例属性: 存储属性

                   计算属性(重写get方法)

          类型属性: (static修饰的)

     

     方法:实例方法:     (可以被重写)

          static类方法:(不能被重写)

          class类方法: (可以被重写)

     */

     

    //属性===========================

    // 存储属性

    //计算属性:需要重写get方法;不用重写set方法

     

    //类型属性 static修饰

     

    //方法===========================

    //实例方法(可以被重写)

    //类方法(类型方法,不能被子类重写) static

    //类方法(能被子类重写)class

     

    14、监听属性改变

    /**

      oc中通过重写set方法监听属性改变

      swift中可以通过属性观察者来监听和响应属性值的变化

          通常是监听存储属性和类属性的改变 willSet方法(newValue) didSet方法(oldValue)

          计算属性不需要定义属性观察者,可以在计算属性的get方法中监听属性变化

     */

     

     

    15、swift中自动引用计数

    weak

    //swift是ARC,没有MRC一说;

    //swift循环引用

     

    16、三大特性

    1》注意swift的重载,当根类继承自NSObject,则重载的方法可能会报错,因为继承自NSObject后,说明这个类有可能和oc进行混编,而oc是不支持重载的某些情况例如:方法名相同+参数个数相同 + 参数类型不同+返回值相同的情况,此时需要在此重载的函数上加 @(newMethodName:) 即在混编为oc时此方法变为newMethodName:

    // 重载

        func chongzai(a:Int) -> Int {//1

            return a

        }

        @objc(chongzaiTwo:)

        func chongzai(a:Int) -> Double {//返回值不同 2

            return 0.2

        }

        @objc(chongzaiThree:)

        func chongzai(a:Double) -> Int {//参数类型不同 3

            return 1

        }

        @objc(chongzaiFour::)

        func chongzai(a:Int,b:Int) -> Int {//参数个数不同 4

            return 1

        }

    //根类Person继承自NSObject后,说明此类及此类相关类以后可能会混编成oc,故2和3都要加通过@objc()为函数添加别名,注意参数,4处没必要加,这里只是看下俩参数的情况是两个冒号

     

    2》注意swift的重载,当两个重载的函数  函数名相同+参数数目相同+参数类型相同+参数名相同+返回值类型不同的情况,在调用这种类型的函数时,一定要标明变量的类型

    let b:Double = s1.chongzai(a: 2)//需要加类型 :Double

    let c:Int = s1.chongzai(a: 3)//需要加类型 :Int

     

    16、三大特性-多态

     

    17、结构体和类的区别

    1》结构体有逐一构造器,类没有

        结构体和类都有static类型方法,结构体没有class类方法而类有;

    2》结构体是值类型,类是引用类型

    3》结构体不能继承(即没有多态)

    枚举结构体都可以遵守协议,但不能继承

     

    18、Any,NSObject,AnyObject区别

    /**

     

      public typealias Any = protocol<> //一个协议声明

      @objc public protocol AnyObject{} //一个具体的协议,协议里面没有内容;默认情况下,所有的类,都遵守这个协议;注意所有的类

      open class NSObject : NSObjectProtocol //所有继承自NSObject的类及NSObject类

     

      范围从大到小:Any > AnyObject > NSObject

     

     */

    //注意Int,Double,String在swift中都是结构体类型

     

    20、可选链的使用和练习

     

    //以下就是一个可选链,即在包含有多个可选类型的表达式中的任意一个可选类型的值为nil,则会导致整个表达式为nil

    //反之,如果可选链的结果是nil,就代表,调用失败(说明链条中至少有一环节,断了,即表达式中的任意一个可选类型的值为nil)

    // () == Void != nil  void类型的函数

    let res = per.dog?.toy?.price()

     

     

     

    21、APP讲解

     

    架构:

    MVVM + RAC

    MVP

    VIPPER

    组件化

     

    数据结构:二叉树

    第三天 

     

    05、协议的基本使用

    /**

     swift的类,枚举,结构体都能遵守协议;oc只能类遵守协议

     */

     

    //1=========类遵守协议

    swift中的协议被遵守后,协议方法必须实现

    class Graduate:Animal,Work{//继承父类,并遵守协议,swift的类不支持多继承

     

    //2============枚举遵守协议

     

    //3============结构体遵守协议

     

    //5============协议之间的继承

    protocol momanwork:Work {

     

    //类中使用协议方法:一般设置代理使用

    //枚举中使用协议方法:直接用枚举中的实例调用 One.left.run()

    //结构体中使用协议方法:直接用结构体的实例调用 let tw:Two = Two()  tw.run()

     

    06、协议中代理的使用

    //协议在类中的使用:(注:05中介绍了枚举和结构体中协议的使用,而类以代理的方式使用协议)

    delegate用weak修饰防止死循环:

    //   1、这样写会造成死循环,故要加weak修饰

    //   2、加weak修饰后报错(weak只能修饰类): 'weak' must not be applied to non-class-bound 'Work'; consider adding a protocol conformance that has a class bound

    //   3、weak解决方案一:让Work继承自class类

    //   4、weak解决方案二:让Work继承自NSObjectProtocol协议,由于swift中必须实现协议的所有方法,因为NSObject已经遵守了NSObjectProtocol协议,因此让遵守此协议的所有类都继承自NSObject类即可

    //    还是觉得方案一比较好

     

    07、协议中的可选

    //协议的可选,仅仅是oc的特性,swift不支持

     

    //解决方案:让swift协议,拥有oc特性

     

    @objc

    protocol Work{

      @objc optional func work()

    }

     

    08、泛型

    Swift强类型语言,必须类型匹配

    /**

     泛型:泛泛的类型,不是一个具体的类型

     

     一旦函数内部,确定泛型的具体类型,那么所有的泛型都是该类型

     

     */

    //应改为地址交换

    func exchangeNum(a: inout Int,b:inout Int){

        let temp = a

        a = b

        b = temp

    }

    var a1 = 1212

    var b1 = 3223

    exchangeNum(a: &a1, b: &b1)

     

    func exchangeFanXing<T>(a:inout T,b:inout T){//一般用T表示泛型//一般用T表示泛型,写其他任意的代号都行

        let temp = a

        a = b

        b = temp

    }

    //var dd = 1.3

    //var ff = 3

    //exchangeFanXing(a: &dd, b: &ff)//a和b类型不一致时就会报错,因为使用泛型时,当第一个泛型类型的值的类型确定后,则所有使用此泛型类型的值都和第一个值相同;即当此处第一个参数的类型为1.3,则默认后边的b也为Double类型,故不能传其他类型的值

     

     

    08、泛型二

    /**

     泛型的使用

     1、与结构体集合

          struct Point<T>{

        var x:T

        var y:T

       }

     2、与类结合

           class Stack<T>{

        

        var nums:[T] = []

        var nud:Array<String> = []

        

        func push(value:T) {

            print("进栈")

            nums.append(value)

        }

        func pop() -> T{

            print("出栈")

           return nums.removeLast()

        }

       }

     

     3、与协议结合

        protocol work{

        associatedtype T

        func eat() -> T

        func run() -> T

      }

    public protocol View : _View {
        associatedtype Body : View
        var body: Self.Body { get }
    }

    SwiftUI 的 View 实现成协议,使用 associatedtype 关联了一个 Body 类型。根据 Swift 的语法,带有 associatedtype 的协议不能直接返回,只能作为类型约束。

       class Person:work{

        func eat() -> Person {

            print("人吃饭")

            return self

        }

        func run() -> Person {

            print("人跑步")

            return self

        }

      }

     

      class Dog:work{

        func eat() -> Dog {

            print("🐶吃肉")

            return self

        }

        func run() -> Dog {

            print("狗跑跑")

             return self

        }

      }

    Person().eat().eat().run().run() 链式语法

     

    Dog().eat().run().run().run().eat() 链式语法

     */

     

     

    /**

     泛型与where语句结合

     */

     

    func test<T>(a:T) where T:Person {

        print("where与泛型")

    }

    test(a: person)

    //test(a: Dog()) //❎

     

    09、闭包的基本使用

    //闭包 = 特殊函数

     

    //1、无参闭包

    var bibaoOne:()->() = {

    }

    bibaoOne()//闭包调用

     

    var bibaoTwo:()->(Int) = {

        return 10

    }

    let aa = bibaoTwo()//闭包调用

     

    //2、有参闭包

    var bibaoThree:(Int)->(Int) = {

        (a:Int)->(Int) in

        return 23

    }

    let b = bibaoThree//此表达式只是查看类型,并非闭包的调用(Int) -> (Int)

    let bb = bibaoThree(2)//闭包的调用

     

    /**

     无参闭包  不用写  in及in前边的东西

     有参闭包   要写   in及in前的东西

     (a:Int,b:Int)->(Int) in  或  (a,b)->(Int) in  等价

     */

     

     

    //1、当函数使用:上边都是直接当参数的例子

    //2、当参数使用:

    func exec(a:Int,b:Int,block:(Int,Int)->(Int)) -> Int{

        return block(a,b)

    }

    exec(a: 10, b: 3, block: bibaoFour)//qqq

     

    exec(a: 2, b: 9) {//ooo   尾随闭包

        (m,n)->(Int) in//(m:Int,n:Int)->(Int) in

        return m + n

    }

     

    //闭包的{}是闭包的具体实现

    //qqq中的bibaoFour相当于ooo中的{}及其内部内容

     

    //非尾随闭包,下边细讲

    func execOne(a:Int,block:(Int,Int)->(Int),b:Int) -> Int{

        return block(a,b)

    }

    execOne(a: 2, block: { (a, b) -> (Int) in

        

    }, b: 4)

     

     

    10、参数闭包-尾随闭包和逃逸闭包

     

    尾随闭包:

    /**

     

     闭包做参数时,分尾随闭包和非尾随闭包

     尾随闭包:在函数中做参数时,是最后一个参数

     非尾随闭包:在函数中做参数时,非最后一个参数

     

    */

    另外 Swift 中,假如函数调用中只有一个参数,并且这个参数是个 closure(闭包),可以省略函数调用的圆括号,例如

    VStack {
        Text("Hello World")
        Text("Hello SwiftUI")
        Text("Hello Friends")
    }

    实际上也是调用了 VStack 的 init 函数。注意 VStack 的 init 函数,某些参数有默认值,因而某些参数可以省略不写

    同理

    ForEach(romes)  { rom in
        xxx
    }

    这种语法也是调用了 ForEach 的 init 函数,生成一个 ForEach 对象,ForEach 也是个 View。

     

    //设计函数时,闭包尽量放在最后

     

    逃逸闭包:

    /**

     oc里

     block是对象

     在block中使用强指针,会额外的生成一个该对象的强引用,闭包也是

     */

     

    //逃逸闭包==========================

    当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。

    使闭包逃逸出函数的方法:使闭包被函数外部的某个变量持有即可。https://www.jianshu.com/p/d386392fa8c0

    /**

      如果一个函数的参数,是一个闭包类型,那默认情况此闭包是”非逃逸闭包“,此闭包的生命周期是函数的生命周期,即其生命在函数的右大括号结束

      在闭包前加 @escaping

     */

     

     

    11、在10里写了

     

    12、闭包的循环引用及解决方案

    oc:weak与unsafe_unretained 的区别

    Weak在指针置为nil时,直接释放地址,地址变为0x00;而unsafe_unretained不会释放释放地址,还是以前的地址,所有后者会造成野指针错误

     

    weak

    unowned

    [self weak];

     

    /**

     通过组一组二比较:

        了解闭包做属性即非可选闭包做属性,闭包属性的类型和闭包属性返回值的类型

        闭包为可选类型,则闭包的返回值也为可选类型

        可选型闭包做属性的比较非可选型闭包做属性

     

     闭包循环引用的处理方法:

        循环引用原因:强指针在被block内部调用时,都会生成一个强引用,早成循环引用,因此block或闭包内需要引用的强指针都要弱化

       1> 闭包外使用weak     weak var weakSelf = self 注意此方式闭包内的weakSelf是可选类型

       2> 闭包外使用unowned   unowned let weakSelf = self 注意此方式闭包内的weakSelf是 非 可选类型

       3> 闭包内使用[weak self] in  使用self是可选类型  let dd = self//let dd: Person?

       4> 闭包内使用[unowned self] in 使用self是非可选类型  let dd = self//let dd: Person

     

     */

     

    1> 闭包外使用weak

      (weakSelf?.num)! + 10//在用weakSelf调用num时,系统会自动加?,原因是:weak修饰的对象,在对象释放后会自动转为nil,因此weak修饰的对象后,此对象会变成可选类型,故系统默认加?

      //  weakSelf?.num + 10//报错,因为weakSelf是可选类型,其属性num也会是可选类型,用可选类型直接不能和非可选类型直接运算,需要将num转为非可选类型

     

    2> 闭包外使用unowned

      //unowned 相当于oc的 __unsafe_unreatined,(注:一般情况当用__unsafe_unreatined有野指针风险)

     

    3>闭包内使用[weak self] in

      [weak self] in//相当于把下边的self全部改成weakSelf

      self?.num = 1//可选类型self?.num可以直接赋值

       self?.num = 1//可选类型self?.num可以直接赋值

      (self?.num)! + 2//可选类型self?.num不能直接运算需要解包才行即!,这里没有判断用的是强制解包(有危险)

      //(self?.num)! += 3//可选类型self?.num不能直接运算需要解包才行即!,这里没有判断用的是强制解包(有危险)//直接 表达式 += 或-= 等会出问题,要用变量接收下此表达式再进行 += 或-=运算

      var ds = (self?.num)!

      ds += 3 

      print(self?.num ?? 0)//空合运算符当self.num为nil时,给默认值0

     

    4>闭包内使用[unowned self] in

        [unowned self] in//相当于把下边的self全部改成weakSelf

     

    13、懒加载

    lazy var dog:Dog

     

    懒加载方式:

     1、构造函数获取实例

     2、一般函数获取实例

     3、闭包获取实例:一般情况:省略变量名,用$0

     

    懒加载的nil

    oc的懒加载,在懒加载属性置为nil后,再次调用此属性,会重新走懒加载逻辑,为此属性赋值

    swift的懒加载,只在第一次访问此懒加载属性时,会调用相应的函数,获取实例,下次即使为nil,也不会再次调用相应的函数获取新的实例

     

    swift的注释:

    1、文档注释:///;文档注释的快捷键option + cmd + /

    2、分块注释:// #pragma -mark-

    3、待做://TODO: - 需要接着做

    4、待解决bug:// FIXME:解决bug

    注意swift中要想3、4两种注释有警告提醒,需要写js脚本,而oc则不用写脚本

     

    14、swift的访问权限

     oc的访问权限:

      @private:作用范围只能在自身类

      @protected:作用范围在自身类和继承自己的子类,什么都不写,默认是此属性

      @public:作用范围大,在任何地方

      @package:本包内使用,跨包不可以

      注意:以上只是用来修饰成员变量;无法修饰方法

      .h中@interface中声明的成员变量默认是@protected;

       .m中@interface中声明的成员变量和@implementation中声明的变量默认是@private

       oc权限标记符的作用域是所有后边的成员变量,除非下边又明确指明权限

       oc的权限符只能用于成员变量,不能用于方法

     

    swift中的访问控制模型基于:模块、源文件、类 这三个概念

     interval:在本模块中都可以进行访问(默认;子类也可以继承)

     private:当前类私有;修饰方法;只能访问当前源文件中的实体,切不能访问扩展中对象(注意Swift中的private和其他语言不太一样,它是基于源文件的,作用范围是整个源文件,如果一个源文件中有两个类,那么一个类可以访问另外一个类的私有成员).

     fileprivate:在当前文件私有;修饰方法 可访问当前源文件中实体,包括扩展.

     public:如果修饰类,则无法继承;修饰方法

     open:如果修饰类,可以继承;修饰方法

     

     同一工程就是一个模块;工程中代码和工程中framework是不同的模块

     swift只有module(模块)才需要import 

     swift权限标记符的作用域只有一行

     

    16、异常处理

    //Error

    //是一个内容为空的协议,主要是让编辑器知道,是可以表达异常的

    //如果用异常则可以不用可选类型,因为一般抛异常会处理这些为nil的场景

    //如果想成为异常值,必须遵守Error协议

     

     

    //如果用到了能抛异常的方法,则需要在do下try尝试捕捉异常,如果try捕捉到异常则执行下边的catch,然后再抛异常

      do{

            content = try String(contentsOfFile: path)

        }catch{

            throw FileError.UnAvailable

        }

        

     

    处理异常:

    注意:如果用playground测试资源,需要把资源拖进工程

    处理方式一:do catch 抛异常,关心异常的具体内容

    do {

       let content1 = try readFile(path: path)

    } catch  {

        print(error)

    }

    处理方式二:try? 知道这里可能会抛异常,但是就是不想处理,如果有问题则为nil可选类型,仅仅关心结果

    let content2 = try? readFile(path: path)

    处理方式三:try! 确信及肯定没有任何异常,任何能导致异常的条件,均不满足

    let content3 = try! readFile(path: path)

     

     

    14、swift工程中调用oc文件:

    需要创建桥接文件,将oc文件导入到桥接文件中(此过程将oc代码转变为swift代码),即可在swift中使用

     

    15、oc工程中调用swift文件

    在调用oc的文件中导入一个头文件:项目名字-Swift.h

    使用注意:1、如果想让Swift类/方法/属性,在oc中使用;需要使用public关键字对类/方法/属性/协议等等进行修饰;swift代码是一个module

                   2、如果是类,必须继承自NSObject

                   3、如果是协议,协议必须加@objc,让swift协议拥有oc特性,即让协议的方法可选;并让协议继承NSObjectProtocol

     

    15、playground的高级用法

     Quick Look:快速查看运行结果,例如颜色,图片尺寸等

     

     Sources目录:1、在playground中写的代码,会被事实编译,并运行将结果显示出来

                 2、每次只要修改一个字符,都会重新编译

                 3、解决方案:放到Sources目录下的源文件会被编译成module并自动导入到playground中,只会编译一次

                 4、使用注意:需要使用public关键字修饰资源文件中,需要暴露给外界的内容

     

     资源即Resources目录:

                 1、单一的layground并不是一个完整的APP,所以并没有使用沙盒机制

                 2、如果在playground中使用某些资源例如图片资源,需要放在Resources目录下

                 3、Resources分为:

                                独立资源:放在Resources目录,放到此目录下的资源每个Playground是独立的,可以通过mainBundle访问获取

                                共享资源:共享资源是用户放在Documents目录下的,通过XCPlaygroundSharedDataDirectoryURL来获取共享资源目录的URL;注意要先导入XCPlayground模块

     

     异步执行:

     Playground代码自上而下执行,并在执行完之后立即停止

     所以,如果想在playground中测试一些异步处理(比如网络请求)一般情况,就无法实现

     解决方案:

     1》导入模块 PlaygroundSupport

     2》让Playground永远执行

     3》在异步让Playground停止执行

     //不加这句PlaygroundPage.current.needsIndefiniteExecution = true只打印ooo不打印dd

    //加PlaygroundPage.current.needsIndefiniteExecution = true之后,则会走异步方法,打印dd

     

    多页面:把不同代码,放在不同界面;界面间跳转;注:这里的跳转是代码之间不同文件的切换

          File-》new-》Playground Page-》Render Documentation 则创建page和打开跳转

    正常跳转代码://: [Previous](@previous)  //: [Next](@next)

    也可以自定义跳转  //: [随便写](页码数注意没有@)  //: [Next](@next)

    注意:playground支持markdown语法,所以默认是以markdown语法的格式展示//: [Previous](@previous)即为markdown语法

     

    TimeLine:时间轴 xcode10看不见这个选项了

     

     

    链式编程

    综合项目:

    6.12

    Swift无宏用全局常量

    Swift不支持非同种基本数据类型的运算

    //        itemSize = CGSize(width: width, height: height) //xcode11beta的itemSize属性不好使了,换用estimatedItemSize就好使了,坑死个人

    //   xcode11的collectionViewcController的cell多了一层contentView

            estimatedItemSize =  CGSize(width: width, height: height)

     

    6.13

    cocoapods:

    如果用swift最好在创建podfile时,打开useframeworks选项,第三方以.a的形式加进工程,因为swift是可以import module直接使用的,如果不打开useframeworks选项,则第三方以.a的形式加进工程,而.a需要.h才能使用,所以使用时还需要搞桥接文件,在桥接文件中import 此第三方的.h文件(在swift工程中创建任意的oc文件就会产生桥接文件,在这我们只需要使用桥接文件,此处生成的oc文件直接删除即可)

     

    http://api01.idataapi.cn:8000/hotel/idataapi?city=北京&brandName=希尔顿欢朋&apikey=XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog

     

     

    UICollectionViewFlowLayout的parepare()方法:

    当collectionview,想要使用布局对象,布局里面的cell的时候,会自动调用布局对象的prepare方法

    6.14

    封装请求及布局文件

     

    viper设计模式

    设计模式:开发了很多个项目,总结出来的模式,用起来方便耦合性低

    //    func loadData() {//版本一

    //        let manager = AFHTTPSessionManager()

    //

    //        let param:[String:Any] = ["city":"北京","brandName":"希尔顿欢朋","apikey":"XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog"]

    //

    //        manager.get("http://api01.idataapi.cn:8000/hotel/idataapi", parameters: param, progress: nil, success: {

    //            (task: URLSessionDataTask, responseObj: Any?) -> Void in

    //

    //             print("responseObj:\(String(describing: responseObj))")

    //             var models = [HomeListModel]()

    //            if let resonse = responseObj as? [String:Any]{

    //                if let dicArray = resonse["data"] as? [[String:Any]] {

    //                    for dic in dicArray {

    //                        let model = HomeListModel(dic: dic)

    //                        model.imageUrls =  dic["imageUrls"] as? [String]

    //                        models.append(model)

    //                    }

    //                }

    //            }

    //            self.modelsArray = models

    //

    //        }) {

    //            (task: URLSessionDataTask?, error: Error) -> Void in

    //

    //            print("error:\(error)")

    //

    //        }

    //

    //    }

        

    //    func loadData(){//版本二

    //        let param:[String:Any] = ["city":"北京","brandName":"希尔顿欢朋","apikey":"XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog"]

    //

    //        NetworkTool.request(type: .GET, url: "http://api01.idataapi.cn:8000/hotel/idataapi", param: param) { (responseObj:Any?, error:Error?) in

    //

    //            print("responseObj:\(String(describing: responseObj))")

    //            var models = [HomeListModel]()

    //            if let resonse = responseObj as? [String:Any]{

    //                            if let dicArray = resonse["data"] as? [[String:Any]] {

    //                                for dic in dicArray {

    //                                    let model = HomeListModel(dic: dic)

    //                                    model.imageUrls =  dic["imageUrls"] as? [String]

    //                                    models.append(model)

    //                                }

    //                            }

    //                             self.modelsArray = models

    //            }

    //            if let lfail = error {

    //                print("error:\(lfail)")

    //            }

    //

    //        }

    //    }

        

        func loadData(){//版本三

            HomeNetworkTool.homeRequest {

                (modelsArray : [HomeListModel]?) in

                

                if let newmodelArray = modelsArray {

                    self.modelsArray = newmodelArray

                }

                

            }

            

        }

     

    @available的用法:

    @available放在函数(方法),类或者协议前面。表明这些类型适用的平台和操作系统。

     

    6.17

    新版的mac10.14.5安装ruby失败,也能正常使用cocoapods

    swift中用Masonry

    https://dimg04.c-ctrip.com/images/230s13000000v2sl4B9DD_W_1280_853_R5_Q70.jpg

    http://dimg04.c-ctrip.com/images/230s13000000v2sl4B9DD_W_1280_853_R5_Q70.jpg

     

    Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

    不能再通过storyboard得到视图控制器时赋值,说明通过storyboard得到的视图控制器并未初始化。

     

    6.19 模拟器的debug-slow animation减缓动画效果

    Xcode11创建的工程(注意用旧xcode创建出来的,用xcode11打开不行),present(push的暂时未知)出来的控制器在看层级图时,能看见将此控制器推出来的之前的控制器,这两个控制器之间有个Transitionview,并且被present出来的控制器包在此Transitionview内

    旧版Xcode只有在detailCollecVc.modalPresentationStyle = .custom情况下才会出现此Transitionview,因此我们在做复杂的推出动画时要设置modalPresentationStyle为custom

     

    6.20

    colelctionview的坐标转换:

    把cell的frame转换到window上,这是UIView的功能

     let fromFrame = collectionView.convert(curentCell.frame, to: window)

     

    6.29:Swift函数的return可以省略,直接写返回的数据即可。

     

    7.2:

    Attribute

    Attribute 是指 @ 字符开头的,类似 @available 这种语法。

    Swift 的 Attribute 语法可以放到类型定义或者函数定义的前面,是对类型和函数的一种标记。

    Attribute 的原理:

    编译 Swift 源代码时,在解析阶段(Prase), 会生成一个抽象语法树(AST,Abstract Syntax Tree)。语法树生成时,所有的 Attribute 统一处理,生成 Attribute 节点。之后在语义分析阶段(semantic analysis),会有可能触发 Attribute 节点,使其对语法树本身产生影响。

    不同的 Attribute 对语法树可以有不同的影响。比如 @available 会根据系统对语法树的函数调用进行可行性检查,不修改语法树本身。而 @dynamicMemberLookup,@dynamicCallable 进行检查后,可能会直接修改语法树本身,从而转调某些根据规则命名好的类或者函数。

    Attribute 是种元编程(Metaprogramming)手段,Attribute 语法会被编译成语法树节点,而 Attribute 又可以反过来修改语法树本身。在类定义或函数定义前添加不同的 Attribute,可以不同的方式修改语法树,从而实现某些常规方式难以实现的语法。其实很好理解,既然都可以修改语法树了,自然就可以通过 Attribute 实现神奇的语法。

    假如修改 Swift 的源码,可以根据不同的场合,很容易添加自定义 Attribute。比如 @UIApplicationMain 就是一个自定义 Attribute 扩展,为语法树添加了入口 main 函数。因而用 swift 写 iOS App, 是不用自己写 main 函数的。

     

     

    12.15:

    逃逸闭包、自动闭包  https://www.jianshu.com/p/d386392fa8c0

    闭包是引用类型

    无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用 incrementByTen和incrementBySeven 是一个常量,而并非闭包内容本身。

    将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self(即调用属性要用self.的格式)普通的闭包则不用不用self.的形式调用属性,即可以隐式的调用属性.

    传递到 someFunctionWithNonescapingClosure(:) 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用 self

    @noescape @escaping @autoclosurehttps://www.ucloud.cn/yun/16463.html

    非逃逸闭包:@noescape:函数结束后,这个闭包的生命周期也将结束;

           当闭包作为参数传递进函数时,如果这个闭包只在函数中被使用,则开发者可以将这个闭包声明成非逃逸的,即告诉系统当此函数结束后,这个闭包的生命周期也将结束,这样做的好处是可以提高代码性能,将闭包声明成非逃逸的类型使用@noescape关键字。

    (1) 默认,swift 3.0 弃用,函数结束后,这个闭包的生命周期也将结束。

    (2) 在其内部如果需要使用self这个关键字,self可以被省略

    逃逸闭包:@escaping 逃逸的闭包常用于异步的操作,这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用;

           例如这个闭包是异步处理一个网络请求,只有当请求结束后,闭包的生命周期才结束。当闭包作为函数的参数传入时,很有可能这个闭包在函数返回之后才会被执行。

    自动闭包:@autoclosure 默认非逃逸;闭包也可以被自动的生成;不接受任何参数;自动闭包能够延迟求值;

    (1)默认非逃逸

    (2)闭包也可以被自动的生成,这种闭包被称为自动闭包,自动闭包自动将表达式封装成闭包。

    (3)自动闭包不接收任何参数,被调用时会返回被包装在其中的表达式的值。

    (4)当闭包作为函数参数时,可以将参数标记 @autoclosure 来接收自动闭包。

    (5)自动闭包能够延迟求值,因为代码段不会被执行直到你调用这个闭包。

    (6)自动闭包默认是非逃逸的,如果要使用逃逸的闭包,需要手动声明: @autoclosure @escaping 旧版本:@autoclosure(escaping)

     

    12.18

    https://www.cnblogs.com/comsokey/p/Swift1.html

    自定义运算符:/ = - + * % < >!& | ^。~

    前置运算符    prefix

    中间运算符    infix

    后置运算符    postfix

     

    12.21

    Swift与集合有关的高阶函数:https://www.jianshu.com/p/7f4472c1b039

    高阶函数:仅仅是一个函数,可以接收函数做参数或者返回一个函数来操作其他其他函数

     

    Swift自动为内联函数提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序调用闭包参数

    如果在闭包表达式中使用参数名称缩写,则可以在闭包参数列表中省略对其定义,并且对应参数名称缩写类型会通过函数类型进行推导。in关键字也可以被省略。

    map() 函数:

    filter() 函数:

    reduce() 函数:

    flatmap() 函数:

    高阶函数的意义:阅读和理解复杂的函数式编程;编写更优美、更易于维护的代码,具有更好的可读性;提高我们的Swift语言能力

     

    Swift iOS 13 启动兼容13以前低版本UI state restoration for a scene in iOS 13 while still supporting iOS 12. No storyboards

    https://stackoverflow.com/questions/57129668/ui-state-restoration-for-a-scene-in-ios-13-while-still-supporting-ios-12-no-sto

     

     

    12.22

    1、swift多态:https://www.jianshu.com/p/02bb7658a998

    Swift引用变量有两个类型:

    编译时类型:等号左边,声明变量的类型

    运行时类型:等号右边,实际赋值给该变量的类型

    多态性:

    编译时多态:方法重载;

    运行时多态:p.toString(),不能确定是父类的toString()方法,还是子类的,运行时再确定

    is检测类型:判断前边的引用类型变量是否是后面的类或子类、实现类的实例

    使用as向下转换类型:

    as强制将运算符前面的引用变量转换成后面的类型,转换失败,则程序出现运行错误

    as? 可选类型的向下转换,可选值包含nil

    1、向下转换只能在具有继承关系的两个类型之间进行。

    2、考虑到进行强制类型转换时可能出现异常,因此进行类型转换之前可先通过is运算符来判断是否可以成功转换。这样可以使程序更加健壮。

    3、当把子类实例赋给父类引用变量时,被称为向上转型,这种转型总是可以成功的。

    向下转好像没啥意义;向上转是自动的

     

    Any和AnyObject:

    swift为不确定类型提供了两种特殊的类型别名

    1、AnyObject:可代表任何类的实例。

    2、Any:可代表任何类型,包括Int、Double等值类型。

     

    嵌套类型:

    swift允许在一个类型的内部(即{}内部)定义另一个类型,这种机制被称为嵌套类型。

    需要说明的是,嵌套类型不允许使用static或class修饰,也就是说,嵌套类型没有所谓的“类型相关的嵌套类型”。

     

    扩展:

    扩展是swift的一种动态特征,swift允许使用扩展为现有的类添加新方法,并且不需要创建子类,不需要访问原有类的源代码。但因为扩展不是派生子类,所以扩展不支持重写。

     

    协议:

    用于定义多个类型应该遵守的规范。swift协议的作用,就相当于其他语言中接口的作用。

     

    合成协议:

    swift允许将多个协议合成一个临时的类型,这种用法被称为合成协议。

     

    唯类协议:

    这种协议只能被类实现,不能被枚举、结构体实现。其定义方式,只要在定义协议的协议名后的冒号后面添加class关键字即可(class放在所有父协议的第一位)。

     

    可选协议:

    可以选择不实现的协议。可选协议必须添加@objc修饰,协议成员前添加optional关键字即可定义可选协议。可选协议一定是唯类协议(因为它只能被类实现,不能被枚举、结构体实现)

     

    注意:

    1. 协议支持形参个数可变的方法,但不支持为形参指定默认值。

    2. 如果协议中定义的方法没有使用mutating修饰(没有变成可变方法),枚举、结构体实现该协议,并实现该方法是就不能添加mutating。简单的说,可变、非可变方法都可实现协议中的可变方法;但只有非可变方法才能实现协议中的非可变方法。

    3. 协议能要求实现者必须提供哪些下标,也能要求该下标是否有setter部分和getter部分。

    4. 只有使用类实现协议中定义的构造器时,才需要使用required;使用枚举、结构体实现协议中的构造器则没有这些要求。

     

    2、convenience关键字:

     

    3、Final关键字:https://blog.csdn.net/zkdemon/article/details/90265451

    Swift中,final关键字可以在class、func和var前修饰。表示 不可重写 可以将类或者类中的部分实现保护起来,从而避免子类破坏。

     

    4、subscript

    在Swft中,subscript关键字表示下标,可以让class、struct、以及enum使用下标访问内部的值。其实就可以快捷方式的设置或者获取对应的属性, 而不需要调用对应的方法去获取或者存储

    • 下标是方法的一种,是访问集合、列表或者序列中的元素的快捷方式。用法:实例名[索引],可以访问或设置其中元素 https://www.jianshu.com/p/0335ebf8f7d5

    • 定义形式:一个名为subscript的计算属性,可以忽略set(只读)

    subscript(index: 参数类型) -> 返回类型 {

        get{}

        set{}

    }

    常见用法:访问字典、数组、集合的某一个元素

    var topProvinces = ["GD", "JS", "SD", "ZJ"]

    topProvinces[1] //"JS"

     

    var gdpRank = ["JS":70116, "SD":63002, "GD":72813 ,"ZJ":42886]

    gdpRank["GD"]   //72813

     

     

    5、static

    Swift中,用static关键字声明静态变量或者函数,它保证在对应的作用域当中只有一份, 同时也不需要依赖实例化。注意:(用static关键字指定的方法是类方法,他是不能被子类重写的)

     

    6、mutating

    Swift中,mutating关键字指的是可变即可修改。用在structure和enumeration中,虽然结构体和枚举可以定义自己的方法,但是默认情况下,实例方法中是不可以修改值类型的属性。为了能够在实例方法中修改属性值,可以在方法定义前添加关键字mutating

     

    7、extension

    1》定义实例方法和类型方法 2》添加计算型属性和计算静态属性 3》定义下标 4》提供新的构造器 5》定义和使用新的嵌套类型 6》使一个已有类型符合某个接口

     

    8、convenient https://www.jianshu.com/p/3d0507addb30

    swift中,使用convenience修饰的构造函数叫做便利构造函数 。便利构造函数通常用在对系统的类进行构造函数的扩充时使用。便利构造函数有如下几个特点

    1》便利构造函数通常都是写在extension里面  2》便利函数init前面需要加载convenience   3》在便利构造函数中需要明确的调用self.init()  

     

    9、fallthrough

    swift语言特性switch语句的break可以忽略不写,满足条件时直接跳出循环.fallthrough的作用就是执行完当前case,继续执行下面的case.类似于其它语言中省去break里,会继续往后一个case跑,直到碰到break或default才完成的效果

     

    10、open

    在Swift中,open修饰的对象表示可以被任何人使用,包括override和继承。

     

    11、public

    在Swift中,public表示公有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。但在其他模块(一个App就是一个模块,一个第三方API, 第三等方框架等都是一个完整的模块)不可以被override和继承,而在本模块内可以被override和继承

     

    12、internal

    在Swift中,public表示内部的访问权限。即有着internal访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在Swift中默认就是internal的访问权限

     

    13、required

    在Swift里,required是用来修饰init方法的,说明该构造方法是必须实现的。如果子类需要添加异于父类的初始化方法时,必须先要实现父类中使用required修饰符修饰过的初始化方法,并且也要使用required修饰符而不是override

    注意: (1)required修饰符只能用于修饰类初始化方法(2)当子类含有异于父类的初始化方法时(初始化方法参数类型和数量异于父类),子类必须要实现父类的required初始化方法,并且也要使用required修饰符而不是override(3)当子类没有初始化方法时,可以不用实现父类的required初始化方法。

    对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写。https://www.jianshu.com/p/09c6c88ed61e

     

     

    14、open

    在Swift中,open修饰的对象表示可以被任何人使用,包括override和继承

    声明为open的类可以在本文件内被其他类继承,也能在其他文件被其他类继承

     

    15、public

    在Swift中,public表示公有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。但在其他模块(一个App就是一个模块,一个第三方API, 第三等方框架等都是一个完整的模块)不可以被override和继承,而在本模块内可以被override和继承

    声明为public的类可以在本文件内被其他类继承,不能在其他文件被其他类继承

     

    16、internal

    在Swift中,public表示内部的访问权限。即有着internal访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在Swift中默认就是internal的访问权限

     

    17、private

    在Swift中,private私有访问权限。被private修饰的类或者类的属性或方法可以在同一个物理文件中访问。如果超出该物理文件,那么有着private访问权限的属性和方法就不能被访问

     

    18、fileprivate

    在Swift中,fileprivate访问级别所修饰的属性或者方法在当前的Swift源文件里可以访问

     

    19、private(set)    https://www.jianshu.com/p/9fcb13990f09

    //声明一个String类型,名字为helloWord的可读可写属性。

    var helloWord: String

    //声明一个String类型,名字为helloWord的外部只读属性,内部可读可写。

    private(set) var helloWord: String

     

    20、rethrows https://xiaozhuanlan.com/topic/3174568902    https://www.jianshu.com/p/802ff8969952

    针对的不是函数或者方法本身,而是他携带的闭包类型的参数,当他的闭包类型的参数throws的时候,我们要使用rethrows将这个异常向上传递

    有点蒙蔽

     

    21、then https://www.jianshu.com/p/f893a0041b07

    https://www.cnblogs.com/weiboyuan/p/10551744.html

    Then是一个语法糖

    Then是一个开源的swift框架,使懒加载的写法更美观和方便

     

    swift的then库

    let label = UILabel().then {

      $0.textAlignment = .center

      $0.textColor = .black

      $0.text = "Hello, World!"

    }

     

    let label: UILabel = {

      let label = UILabel()

      label.textAlignment = .center

      label.textColor = .black

      label.text = "Hello, World!"

      return label

    }()

     

    12.23 

    1、Scanner字符串扫描类

    https://blog.csdn.net/ShmilyCoder/article/details/77948798

    Scanner继承自NSObject,遵守NSCopying协议。是一个用于扫描指定字符串的抽象类;可以创建Scanner时制定他的String属性,然后scanner会按照你的要求从头到尾扫描这个字符串的每个字符;扫描操作从上次扫描的位置开始,并且继续往后扫描直到指定的内容出现为止(如果有的话)

    2、 swift运行时

    1》Swift 运行时机制https://www.jianshu.com/p/b982f4c7b9fc

    纯Swift类的函数调用已经不是OC那样的运行时消息。而是类似C++的vtable,在编译的时候,就决定调用哪个函数了。不像OC在运行时才确定调用哪个函数。对于纯的Swift类来说,无法通过objc runtime替换方法,拿不到这些方法和属性。对于继承自NSObject类(比如NSObject)的Swift来说,将会自动被编译器插入@objc标志。@objc标志是用来将Swift的API到处给Objective-C和Objective-C runtime使用

    加了@objc标识的方法、属性无法保证都会被运行时调用,因为Swift会做静态优化。要想完全被动态调用,必须使用dynamic修饰。使用dynamic修饰将会隐式的加上@objc标识。

     

    2》Swift是否和OC一样有runtime机制  https://blog.csdn.net/judy_luo/article/details/51025210

    可以知道@objc是用来将Swift的API导出给Objective-C和Objective-C runtime使用的,如果你的类继承自Objective-c的类(如NSObject)将会自动被编译器插入@objc标识

    3》HOOK技术https://www.jianshu.com/p/1ba44f9ea71a

    fishhook是facebook出品的一个开源库。利用mach-o文件加载原理,通过rebind_symbols函数修改__DATA Segment的符号指针指向,来动态的Hook C函数。hook OC函数;hook C函数

    Method Swizzle:SEL;id;Class;Method;Ivar(成员变量);IMP;Cache

     UIViewController+swizzling.m 交换 viewdidload方法和自定义的swizz_viewdidload方法;周全起见,有两种情况要考虑一下 第一种情况:要交换的方法并没有在目标类中实现,而是在其父类中实现了, 这时使用class_getInstanceMethod函数获取到的originalSelector指向的就是父类的方法 第二种情况:要交换的方法已经存在于目标类中

     

    3、 swift进阶 Swift进阶https://www.jianshu.com/p/24519d162c01

    成员访问级别约定规则

    1》如果一个类的访问级别是private那么该类的所有成员都是private(此时成员无法修改访问级别),如果一个类的访问级别是internal或者public那么它的所有成员都是internal(如果类的访问级别是public,成员默认internal,此时可以单独修改成员的访问级别),类成员的访问级别不能高于类的访问级别(注意:嵌套类型的访问级别也符合此条规则);

    2》常量、变量、属性、下标脚本访问级别低于其所声明的类型级别,并且如果不是默认访问级别(internal)要明确声明访问级别(例如一个常量是一个private类型的类类型,那么此常量必须声明为private)

    3》在不违反1、2两条规则的情况下,setter的访问级别可以低于getter的访问级别(例如一个属性访问级别是internal,那么可以添加private(set)修饰将setter权限设置为private,在当前模块中只有此源文件可以访问,对外部是只读的);

    4》必要构造方法(required修饰)的访问级别必须和类访问级别相同,结构体的默认逐一构造函数的访问级别不高于其成员的访问级别(例如一个成员是private那么这个构造函数就是private,但是可以通过自定义来声明一个public的构造函数),其他方法(包括其他构造方法和普通方法)的访问级别遵循规则1

    5》子类的访问级别不高于父类的访问级别,但是在遵循三种访问级别作用范围的前提下子类可以将父类低访问级别的成员重写成更高的访问级别(例如父类A和子类B在同一个源文件,A的访问级别是public,B的访问级别是internal,其中A有一个private方法,那么B可以覆盖其private方法并重写为internal)

    6》协议中所有必须实现的成员的访问级别和协议本身的访问级别相同,其子协议的访问级别不高于父协议;

    7》如果一个类继承于另一个类的同时实现了某个协议那么这个类的访问级别为父类和协议的最低访问级别,并且此类中方法访问级别和所实现的协议中的方法相同

    8》扩展的成员访问级别遵循规则1,但是对于类、结构体、枚举的扩展可以明确声明访问级别并且可以更低(例如对于internal的类,你可以声明一个private的扩展),而协议的访问级别不可以明确声明

    9》元组的访问级别是元组中各个元素的最低访问级别,注意:元组的访问级别是自动推导的,无法直接使用以上三个关键字修饰其访问级别

    10》函数的访问级是函数的参数、返回值的最低级别,并且如果其访问级别和默认访问级别(internal)不符需要明确声明

    11》枚举成员的访问级别等同于枚举的访问级别(无法单独设置),同时枚举的原始值、关联值的访问级别不能低于枚举的访问级别

    12》泛型类型或泛型函数的访问级别是泛型类型、泛型函数、泛型类型参数三者中最低的一个

    13》类型别名的访问级别不能高于原类型的访问级别

     

    4、反射:https://www.jianshu.com/p/24519d162c01

    熟悉C#、Java的朋友不难理解反射的概念,所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。在使用ObjC开发时很少强调其反射概念,因为ObjC的Runtime要比其他语言中的反射强大的多。在ObjC中可以很简单的实现字符串和类型的转换(NSClassFromString()),实现动态方法调用(performSelector: withObject:),动态赋值(KVC)等等,这些功能大家已经习以为常,但是在其他语言中要实现这些功能却要跨过较高的门槛,而且有些根本就是无法实现的。不过在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect),即使目前Swift中的反射还没有其他语言中的反射功能强大(Swift还在发展当中,相信后续版本会加入更加强大的反射功能

    在Swift中反射信息通过MirrorType协议来描述,而Swift中所有的类型都能通过reflect函数取得MirrorType信息,获取到一个变量(或常量)的MirrorType之后就可以访问其类型、值、类型种类等元数据信息。

     

    5、dynamic关键字   扩展--KVO https://wwgw.jianshu.com/p/24519d162c01

    和KVC一样,在Swift中使用KVO也仅限于NSObject及其子类,因为KVO本身就是基于KVC进行动态派发的,这些都属于运行时的范畴。Swift要实现这些动态特性需要在类型或者成员前面加上@objc(继承于NSObject的子类及非私有成员会自动添加),但并不是说加了@objc就可以动态派发,因为Swift为了性能考虑会优化为静态调用。如果确实需要使用这些特性Swift提供了dynamic关键字来修饰,例如这里要想使用KVO除了继承于NSObject之外就必须给监控的属性加上dynamic关键字修饰

    注意:对于系统类(或一些第三方框架)由于无法修改其源代码如果要进行KVO监听,可以先继承此类然后进行使用dynamic重写;此外,并非只有KVO需要加上dynamic关键字,对于很多动态特性都是如此,例如要在Swift中实现Swizzle方法替换,方法前仍然要加上dynamic,因为方法的替换也需要动态派发

    swift kvo https://www.jianshu.com/p/f6aa48a11a88

     

    6、指针与内存 https://www.jianshu.com/p/24519d162c01

    除了循环引用问题,Swift之所以将指针类型标识为“unsafe”是因为指针没办法像其他类型一样进行自动内存管理,因此有必要了解一下指针和内存的关系。在Swift中初始化一个指针必须通过alloc和initialize两步,而回收一个指针需要调用destroy和dealloc(通常dealloc之后还会将指针设置为nil)。

    运行程序可以看到p对象在函数执行结束之后被销毁,但是如果仅仅将pointer设置为nil是无法销毁Person对象的,这很类似于之前的MRC内存管理,在Swift中使用指针需要注意:谁创建(alloc,malloc,calloc)谁释放。 当然上面演示中显然对于指针的操作略显麻烦,如果需要对一个变量进行指针操作可以借助于Swift中提供的一个方法withUnsafePointer。例如想要利用指针修改Person的name就可以采用下面的方式:

    var p = Person(name: "Kenshin Cui")

     

    var p2 = withUnsafeMutablePointer(&p, {

        (pointer:UnsafeMutablePointer) -> Person in

        pointer.memory.name = "Kaoru"

        return pointer.memory

    })

     

    println(p.name) //结果:Kaoru

     

    7、扩展—Core Foundation

    Core Foundation作为iOS开发中最重要的框架之一,在iOS开发中有着重要的地位,但是它是一组C语言接口,在使用时需要开发人员自己管理内存。在Swift中使用Core Foundation框架(包括其他Core开头的框架)需要区分这个API返回的对象是否进行了标注:

    1. 如果已经标注则在使用时完全不用考虑内存管理(它可以自动管理内存)。

    2. 如果没有标注则编译器不会进行内存管理托管,此时需要将这个非托管对象转化为托管对象(当然你也可以使用retain()、release()或者autorelease()手动管理内存,但是不推荐这么做)。当然,苹果开发工具组会尽可能的标注这些API以实现C代码和Swift的自动桥接,但是在此之前未标注的API会返回Unmanaged<Type>结构,可以调用takeUnretainedValue()和takeRetainedValue()方法将其转化为可以自动进行内存管理的托管对象(具体是调用前者还是后者,需要根据是否需要开发者自己进行内存管理而定,其本质是使用takeRetainedValue()方法,在对象使用完之后会调用一次release()方法。按照Core Foundation的命名标准,通常如果函数名中含“Create”、“Copy”、“Retain”关键字需要调用takeRetainedValue()方法来转化成托管对象)。

    当然,上述两种方式均是针对系统框架而言,如果是开发者编写的类或者第三方类库,应该尽可能按照Cocoa规范命名并且在合适的地方使用CF_RETURNS_RETAINED和CF_RETURNS_NOT_RETAINED来进行标注以便可以进行自动内存管理。

    8、swift的#selector

     

    12.24swift常用的三方库:

    AFNetworking   Alamofire / Moya

    MBProgressHUD  

    SDWebImage    Kingfisher 

    MJExtension   SwiftyJSON / HandyJSON

    MJRefresh     MJRefresh

    IQKeyboardManager  IQKeyboardManagerSwift

    Masonry      

                          Reachability  检测当前网络连接情况

     

    swift常用框架:

    Alamofire / Moya;

    MBProgressHUD

    Kingfisher  喵神王巍写的一款关于图片下载、缓存的框架,灵感取自于SDWebImage。

    SwiftyJSON / HandyJSON

    MJRefresh

    IQKeyboardManagerSwift

    SnapKit:自动布局框架,类似于Masonry。

     Reachability  检测当前网络连接情况

    SQLite.swift:用swift封装的sqlite 3操作框架。

    MonkeyKing三方分享框架, 社会化分享框架,支持分享text、url、image、audio、file到WeChat、QQ、Alipay、Weibo

     

    swift常用的库

    1、json解析框架:https://www.jianshu.com/p/e9d933ce7c74

    1》SwiftyJSON:本质上仍然是根据JSON结构去取值,使用起来顺手、清晰;但这种做法没能妥善解决上述的几个问题,因为它不是机遇model的,我们使用的时候,依然必须制定key去获取value,这在一定程度上不是很友好

    2》ObjectMapper:实现了JSON直接转Model的功能,不过使用起来,代码量会多一点,因为我们必须遵循Mappable协议,制定json内的每一个key和model属性的对应关系

    3》HandyJSON:是阿里一位大神推出的,能够做到JSON转Model一步到位,而且使用起来,非常简洁方便;HandyJSON另辟蹊径,采用Swift反射+内存赋值的方式来构造Model实例,保持原汁原味的Swift类定义

    2、Moya:https://www.jianshu.com/p/219b197a230a

    Moya是一个帮助我们管理Alamofire的网络管理层,可以让我们去更清晰的去管理我们的网络请求。

     

    深入理解Moya设计 https://segmentfault.com/a/1190000012997081

    swift的Moya虽然也有使用到继承,但是它的整体上是以POP思想(Protocol Oriented Programming,面向协议编程)为主导的;而Swift则引入了面向协议编程,通过协议来规定事物的实现。通过遵守不同的协议,来对一个类或者结构体或者枚举进行定制,它只需要实现协议所规定的属性或方法即可,有点类似于搭建积木,取每一块有需求的模块,进行组合拼接,相对于OOP,其耦合性更低,也为代码的维护和拓展提供更多的可能性。关于POP思想大致是这样,下面是王巍关于POP的两篇文章,值得读一番 

    王巍的博客   面向协议编程与 Cocoa 的邂逅 (上) https://onevcat.com/2016/11/pop-cocoa-1/

    3、Kingfisher:https://www.jianshu.com/p/fa2624ac1959

    网络图片缓存库  https://github.com/onevcat/Kingfisher

    4、SnapKitExtend 相当于Masonry 

     

    5、EmptyDataSet-Swift 当列表页面无数据时不要展示一个白板,显得太空旷,放一个背景图,或者文字提示什么的

     

    Carthage

    使用swift编写,只支持动态框架,只支持ios8+是Cocoa依赖管理工具;与现在流行的 CocoaPods 不同,Carthage编译你的依赖,并提供框架的二进制.framework文件,但你仍然保留对项目的结构和设置的完整控制,Carthage不会自动的修改你的项目文件或编译设置。是一个去中心化的Cocoa依赖管理工具

    安装Carthage和使用: https://www.jianshu.com/p/9c4de4ebbc96

    iOS开发——Carthage:去中心化的Cocoa依赖管理器https://my.oschina.net/u/2524932/blog/729410

    1) CoaoaPods 是一套整体解决方案,我们在 Podfile 中指定好我们需要的第三方库。然后 CocoaPods 就会进行下载,集成,然后修改或者创建我们项目的 workspace 文件,这一系列整体操作

    2) 相比之下,Carthage 就要轻量很多,它也会一个叫做 Cartfile 描述文件,但 Carthage 不会对我们的项目结构进行任何修改,更不多创建 workspace。它只是根据我们描述文件中配置的第三方库,将他们下载到本地,然后用 xcodebuild 构建成 framework 文件。然后由我们自己将这些库集成到项目中。Carthage 使用的是一种非侵入性的哲学

     

    Carthage 基本的工作流程:

    1> 创建一个Cartfile,包含你希望在项目中使用的框架的列表

    2> 运行Carthage,将会获取列出的框架并编译它们

    3> 将编译完成的.framework二进制文件拖拽到你的Xcode项目当中

    Carthage编译你的框架/库,然后提供该框架的二进制文件,但你仍然持有该项目结构和设置的绝对控制。Carthage不会自动的修改你的项目文件或编译设置

     

    Carthage优势:

    ① 使用 Carthage 的话,所有的第三方库依赖,除非是更新的需要,不然平常干净编译 Project,它是不需要再次编译的,大大加快平常编译及 Archive 的时间.

    ② 它是去中心化的,没有中心服务器. 这意味着每次配置和更新环境,只会去更新具体的库,而不会有一个向中心服务器获取最新库的索引这么个过程,如此又省了很多时间.

    ③ CocoaPods 无缝集成!一个项目可同时使用两套包管理工具, 当前 CocoaPods 管理主要 Framework 的配置下, 将少量其他 Framework 交给了 Carthage 管理, 二者可以和谐地共存.

    ④ 结构标准的项目天然就是 Carthage 库

    Carthage不足:

    1⃣️ 库依然不如 CocoaPods 丰富:尽管很多库不需要声明并改造就直接可以被 Carthage 用,但依然有大量 CocoaPods 能用的库不支持,我相信时间能解决这个问题;

    2⃣️ 只支持 Framework,所以是 iOS 8 Only了,随着时间推移,这个也不会是问题;

    3⃣️ 工具仍不完善:在使用过程中,发现它无法在一个结构复杂的项目中正确发现库(比如有 iOS, Mac demo + framework 的结构);

    4⃣️ 无法在 Xcode 里定位到源码:如果你在写代码过程中,想跳转到一个第三方库去看具体的实现,这是无法办到的,Carthage 的配置只能让你看到一个库的头文件

     

    associatedtype

    https://www.jianshu.com/p/2039dc0804c3

     

     

    typealiashttps://www.jianshu.com/p/ac5185c89af1 swift associatedtype和typealias

    为已有类型重命名

    1、可以用来对已有的类型进行重命名,比如在表示两点之间的距离的时候,可以使用typealias将x和y轴的距离Double表示为Distance。

     

    2、可以对闭包进行重新命名,这样在做参数传递的时候更加清晰

     

    3、协议使用associatedtype的时候,可以用来对关联类型重定义,协助协议实现泛型功能,下面会说到

     

    Result<Value, Error> https://onevcat.com/2018/10/swift-result-error/

    它的思想非常简单,用泛型将可能的返回值包装起来,因为结果是成功或者失败二选一,所以我们可以藉此去除不必要的可选值。

     

    Where

    Where关键字后边应该跟条件语句啊?以下where后边是什么用法??

    public extension Reactive where Base: MoyaProviderTyp

    public enum Result<Value, Erroresult.ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible where Error : Error

     

     只给遵守了 WRProtocol 协议的UIView添加了下面的拓展 https://www.jianshu.com/p/1b162ea1660c

    extension WRProtocol where Self:UIView 

    {

        func getString() -> String{

            return "string"

        }

    }

     

    错误处理 try-catch https://www.jianshu.com/p/0335ebf8f7d5

    错误处理:反映运行出错的“细节”,并恢复程序的过程;可选类型仅处理值缺失,错误处理可以针对不同的出错原因对不同的应对

    一个函数可以加上throws关键字,表示可以处理错误,这个函数的调用者可以捕获(catch)这个错误并进行应对

    func method1() throws {

        print("我是一个函数")

    }

    当你调用可以抛出错误的函数,需在前面加上try关键字

    try method1()

    处理更细分的错误情况,错误类型需遵从Error协议

    enum learningObstacle : Error {

        case noMethod, noReading, noTool(withMac: String)

    }

    func iOSLearning(hasMethod: Bool, hasWay: Bool, hasTool: Bool) throws {

        guard hasMethod else {

            throw learningObstacle.noMethod

            //guard语句成立才往后执行,否则抛出错误

        }

        guard hasWay else {

            throw learningObstacle.noReading

        }

        guard hasTool else {

            throw learningObstacle.noTool(withMac: "mac电脑")

        }

    }

     

    var budget = 7000

     

    func purchase(tool: String) {

        //采购

        if budget >= 6000 {

            budget -= 6000

            print("您已采购", tool, ",花费了6000,余额是:", budget)

        } else {

            print("资金不足")

        }

    }

     

    do {

        try iOSLearning(hasMethod: true, hasWay: true, hasTool: false)

        print("成功开始学习")

    } catch learningObstacle.noMethod {

        print("打开慕课网,学习swift语法先")

    } catch learningObstacle.noReading {

        print("看慕课视频")

    } catch learningObstacle.noTool(let mac) {

        purchase(tool: mac)

    }

     

    if let result = try? iOSLearning(hasMethod: true, hasWay: true, hasTool: true) {

        //?可忽略原来写的错误处理

        print("愉快的学习了!")

    } else {

        print("学习失败!")

    }

    //try! 有一项失败程序就中断掉,不推荐

    try! iOSLearning(hasMethod: true, hasWay: true, hasTool: true)

    特定的清理收尾工作,defer语句

    func pk() {

        defer {

            //最后执行

            print("Game Over")

        }

        print("Fight!")

        print("You vs xiaobo ")

        /*

         运行结果

         Fight!

         You vs xiaobo

         Game Over

         */

    }

    pk()

     

    Java的异常处理

    try catch finally

    1、不管有没有出现异常,finally块中代码都会执行;2、当try和catch中有return时,finally仍然会执行;3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的;4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

    https://baijiahao.baidu.com/s?id=1621169426787906677&wfr=spider&for=pc

     

    12.27 whre语句 https://www.jianshu.com/p/50736a304c08

    12.27:

    泛型:

    类型约束:类型约束指定了一个必须继承自指定类的类型参数,或遵循一个特定的协议或协议构成,例:

    func someFunction<T:  SomeClass, U:  SomeProtocol>(someT: T, someU: U)  {  

        // 这里是泛型函数的函数体部分  

    }

    第一个类型参数 T,有一个要求 T 必须是 SomeClass 子类的类型约束;

    第二个类型参数 U,有一个要求 U 必须符合 SomeProtocol 协议的类型约束。

     

     

    open class MoyaProvider<Target: TargetType>: MoyaProviderType 

    MoyaProvider类的泛型Target要遵守TargetType协议,且此类遵守MoyaProviderType协议

     

    假设从A控制器通过present的方式跳转到了B控制器,那么 A.presentedViewController 就是B控制器;B.presentingViewController 就是A控制器。

     

    Where: 

    不同的版本的Swift中where用法有些不同,以最新的Swift4为准

     

    1》extension + where后跟类型(UIImage为例)

    extension Kingfisher where Base: Image

    public typealias Image = UIImage

     

    2》泛型 + where

    func test<T>(a:T) where T:Person

     

    3》where后跟条件表达式

     

    Initializer for conditional binding must have Optional type, not 'String' 条件语句必须为可选类型

     

    swift中where的用法总结:https://www.jianshu.com/p/50736a304c08

    1》if, guard, while三个语句中where被去掉了,直接使用,相隔就行了

    2》do catch

    enum ExceptionError:Error{

        case httpCode(Int)

    }

    func throwError() throws {

        throw ExceptionError.httpCode(500)

    }

    do{

        try throwError()

    }catch ExceptionError.httpCode(let httpCode) where httpCode >= 500{

        print("server error")

    }

    3》switch

    var value:(Int,String) = (1,"小明")

    switch value {

    case let (x,_) where x < 60:

        print("不及格")

    default:

        print("及格")

    }

    4》for in

    for i in arrayOne where dictionary[i] != nil

    5》泛型

    func genericFunction<S>(str:S) where S:ExpressibleByStringLiteral

    // 也可以不使用where语句,直接在尖括号中定义时做限制

    func genericFunction2<S:ExpressibleByStringLiteral>(str:S)

    (注:ExpressibleByStringLiteral是协议)

    6》协议

    extension aProtocol where Self:UIView //只给遵守myProtocol协议的UIView添加了扩展

     

    12.28

    在swift中条件语句可以有多个condition,例如:if case let xs? = t, xs is Int

    swift中的模式匹配:https://juejin.im/entry/596ceaf25188252a0d35cd7e

    switch的主要特性就是模式匹配:

    //swift中的switch的主要特征就是模式匹配

    //普通模式:

            switch 1 {

            case 1:

                print("1")

            default:

                break;

            }

    //1、绑定变量:我们创建karl这个常量时给其设置了name,age,address,我们就可以在switch里面通过值绑定的形式将其取出来。

            enum School{

                case Student(name:String,age:Int,address:String?)

                case Teacher(name:String,age:Int,address:String?)

            }

            let karl = School.Student(name: "karl", age: 27, address: "AH")

            switch karl {

    //我们创建karl这个常量时给其设置了name,age,address,我们就可以在switch里面通过值绑定的形式将其取出来。

    //        case .Student(let name,let age,let address):

    //多个let太麻烦,可以这样写

            case let .Student(name, age, address):

                print("name:\(name) age:\(age) address:\(address!)")

            default:

                break

            }

     

    //2、通配符 _:使用通配符表示忽略这个值,这个值不重要,我不需要使用到它,但是这个也分为两种情况

            let student = School.Student(name: "丽丽", age: 34, address: "CH")

            let teacher = School.Teacher(name: "大爷", age: 55, address: "HB")

            switch student {

            case let .Teacher(name, _, _):

                print("name:\(name)")

            default:

                break

            }

    //        这里第二个参数和第三个参数就是使用了通配符表示,不关心它是什么值,只要前面参数能匹配成功就可以,就算成立

     

        _?    m没必要这样写吧

    //        switch student {

    //        case let .Teacher(name, _, _?):

    //            print("name:\(name)")

    //        default:

    //            break

    //        }

            

    //3、元组

            let people = (age:27,name:"k卡顿")

            switch people {

            case let(_,name):

                print("name:\(name)")

            default:

                break

            }

            switch people {

            case (_,let name):

                print("namename:\(name)")

            default:

                break

            }

            

    //4、模式匹配中使用where

            switch student {

            case let .Student(_, age, _) where age > 18:

                print("成年了:\(age)")

            default:

                break

            }

    //5、匹配range

            let num = 10

            switch num {

            case 0 ... 7:

                print("[0,7]")

            case 10 ..< 20:

                print("[10 20)")

            default:

                break

            }

    //6、类型推断和类型转换

    //  可以通过is判断这个值是否是某个类型。是的话,表示匹配成功;

    //  通过 as可以将这个值尝试转换为某个类型,如果成功,则表示匹配成功

    protocol Animal{}

    struct Dog:Animal{}

    struct Cat:Animal{}

            var animalArray = [Animal]()

            let dog = Dog()

            let cat = Cat()

            animalArray.append(dog)

            animalArray.append(cat)

            for item in animalArray {

                switch item {

                case  is Dog:

                    print("dog")

                case let cat as Cat:

                    print("cat:\(cat)")

                default:

                    break

                }

            }

    //7、模式匹配操作符 ~=

    //   我们可以重载~=这个操作符,让我们自定义switch匹配规则

    //   看下面的代码,我们重载了~=操作符,操作符有两个参数,返回一个Bool值。

    //   参数一:case的值

    //   参数二:switch后的值

    //   返回值:匹配是否成功

            struct Size{

                var width: CGFloat

                var height: CGFloat

            }

            let size = Size(width: 20, height: 20)

            func ~=(caseArea:CGFloat,switchSize:Size) -> Bool{

                return caseArea == switchSize.width * switchSize.height

            }

            switch size {

            case 400:

                print("计算正确")

            default:

                print("计算错误")

            }

    //8、关于Optionals

            let count :Int? = 5

            switch count {

            case 1?:

                print("1")

            case 2?:

                print("2")

            case 3?:

                print("3")

            case _:

                print("nil")

            default:

                break

            }

            //使用?的形式写,算是一个语法糖,可以这么写:

            switch count {

            case Optional.some(1):

                print("1")

            case Optional.some(2):

                print("2")

            case Optional.some(3):

                print("3")

            default:

                break

            }

    //9、 if case let

            let b:Int = 10;

            let a:Int = 10;

            if case let a = b {

                print("这是b的值等于a的情况")

            }

          //      等价于

            switch b {

            case let a:

             print("这是b的值等于a的情况")

            default:

                break

            }

         // 当只有一个条件的时候,用switch会显得冗余,直接用if case let会使代码读起来更便捷。

         // 直接拿上面的绑定变量那一节的内容来说,如果直接这么写的话,会显得更易读,而且也会少点代码

            if case let .Student(name,_,_?) = student {

                print(name)

            }

         //if case let 加 ,

            if case let .Student(name,age,_?) = student,age > 20{

                print(name,age);

            }

            

    //10、 guard case let

          //和 if case let的用法相同,仅仅是guard和if的用法不同

            

    //11、for case let

    //        一个数组里存放的是Any类型的可选值,我想输出这个数组里面大于10的数字。

            let caseArray:[Any?] = [1,2,nil,4,5,"","ss"]

            //如果不用 for case let,代码如下

            for item in caseArray {

                if let element = item as? Int,element > 10 {

                    print("element:\(element)")

                }

            }

            //用for case let 用的是where,而不是逗号

            for case let x? as Int? in caseArray where x > 10{

                print(x)

            }

            //enum School的for case let

            let liLei = School.Student(name: "李雷", age: 12, address: "A")

            let hanMeiMei = School.Student(name: "韩梅梅", age: 23, address: "B")

            let xiaoFang = School.Student(name: "小芳", age: 22, address: "C")

            let maMing = School.Teacher(name: "马明", age: 56, address: "CH")

            let suFen = School.Teacher(name: "素芬", age: 66, address: "CH")

            

            var schoolArray = [School]()

            schoolArray.append(liLei)

            schoolArray.append(hanMeiMei)

            schoolArray.append(xiaoFang)

            schoolArray.append(maMing)

            schoolArray.append(suFen)

    //        获取到数组里的Teacher,并且address不能为空,并且age要大于50的。

            for case let .Teacher(name,age,_) in schoolArray where age > 50 {

                print(name,age)

            }

        

     

    数组遍历:正反向,迭代器,forin https://www.jianshu.com/p/e5a6f1c5764e

     

     

    12.29:

    1、swift中bool的默认值是nil

            var isdd :Bool?

            var iss:Bool = true

            var iff:Bool = false

            print("isdd:\(isdd) iss:\(iss) iff:\(iff)")//isdd:nil iss:true iff:false

     

    2、@discardableResult https://www.jianshu.com/p/af2f062b3d89  https://blog.csdn.net/weixin_34122810/article/details/85996785

      有返回值的方法,没有得到接收合适使用会出现 Result of call to ‘’ is unused 警告,加@discardableResult是为了去除警告;还有一种取消警告的方法,不加@discardableResult直接加通配符_接收方法返回值

    或者在调用函数时,对某些参数不赋值

    3、swift的元类

    T.Type .self理解

     

    extension MoyaProvider {

        @discardableResult

        open func request<T: HandyJSON>(_ target: Target,

                                        model: T.Type,

                                        completion: ((_ returnData: T?) -> Void)?) -> Cancellable? {

            

            return request(target, completion: { (result) in

                Result

                guard let completion = completion else { return }

                guard let returnData = try? result.value?.mapModel(ResponseData<T>.self) else {

                    completion(nil)

                    return

                }

                completion(returnData.data?.returnData)

            })

        }

    }

     

    T.Type

    ResponseData<T>.self

    理解 Swift 中的元类型:.Type 与 .self:

    元类型:就是类型的类型   https://www.jianshu.com/p/36083d0404b9

    1》Swift 中的元类型用 .Type 表示。比如 Int.Type 就是 Int 的元类型

    类型与值有着不同的形式,就像 Int 与 5 的关系。元类型也是类似,.Type 是类型,类型的 .self 是元类型的值

    let intMetatype: Int.Type = Int.self

    2》AnyClass: 系统创建好的变量,代表任何类的元类:具体实现是typealias AnyClass = AnyObject.Type

    获得元类型后可以访问静态变量和静态方法,例如:

    func register(AnyClass?, forCellReuseIdentifier: String)

    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

    这里的 AnyClass 其实就是一个元类型:

    typealias AnyClass = AnyObject.Type,通过上面的定义我们可以知道,AnyClass 就是一个任意类型元类型的别名。

    3》当我们访问静态变量的时候其实也是通过元类型的访问的,只是 Xcode 帮我们省略了 .self。下面两个写法是等价

    Int.max   Int.self.max

    4》type(of:)  与 .self

    type(of:)和.self都可以获得元类型的值,但区别在于:

    let instanceMetaType: String.Type = type(of: "string")//是运行时候的元类型,也就是这个实例 的类型

    let staicMetaType: String.Type = String.self //.self 取到的是静态的元类型,声明的时候是什么类型就是什么类型

    5》Protocol

    ~~protocol MyProtocol { }

    let metatype: MyProtocol.Type = MyProtocol.self~~

    Protocol 自身不是一个类型,只有当一个对象实现了 protocol 后才有了类型对象。所以 Protocol.self 不等于 Protocol.Type

    MyProtocol.Type 也是一个有效的元类型,那么就需要是一个可承载的类型的元类型,所以改成这样:

    struct MyType: MyProtocol { }

    let metatype: MyProtocol.Type = MyType.self 

    Protocol.self 是什么类型?苹果造了一个类型:Protocol

    let protMetatype: MyProtocol.Protocol = MyProtocol.self

     

    元类型可以变得非常灵活和强大,而且在我们编写某些框架性的代码时会非常方便

     

     

    HandyJSON的Metadata.swift文件中

     if !(superclass is HandyJSON.Type) && !(superclass is HandyJSONEnum.Type) {

                    return nil

      }

    将HandyJSON是个协议,但却没用.Protocol;我换成Protocol后依旧能正常运行

     

     

     

    4、swift中的swift 中 Self 与self https://www.jianshu.com/p/a6bcdebd83f5

    在swift 开发过程中,尤其是第三方库中,我们多次看到首字母大写的Self,很多时候不明白其中意思,Self 与self 又有何区别:

    1>.self可以用在类型后面取得类型本身,也可以用在实例后面取得这个实例本身

    2>Self 不仅指代的是 实现该协议的类型本身,也包括了这个类型的子类

       protocol MProtocolTest01 {

     

        // 协议定一个方法,接受实现该协议的自身类型并返回一个同样的类型

        func testMethod(c: Self) -> Self

     

        //不能在协议中定义 范型 进行限制

        //Self 不仅指代的是 实现该协议的类型本身,也包括了这个类型的子类

       }

    例:

    protocol Copyable {

        func copy() -> Self

    }

     

    class MMyClass: Copyable {

        var num = 1

        func copy() -> Self {

            let result = type(of: self).init()

            result.num  = num

            return result

        }

     

        //必须实现

        //如果不实现:Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer。错误

        required init() {

        }

    }

     

     

    12.30

    swift的引用类型和值类型  https://mp.weixin.qq.com/s/Z5txegks7-he2hhSj_DRbg

    内存(RAM)中有两个区域,栈区(stack)和堆区(heap)。在 Swift 中,值类型,存放在栈区;引用类型,存放在堆区

    值类型:

    struct、enum、tuple

    Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理;

    如果声明一个值类型的常量,那么就意味着该常量是不可变的(无论内部数据为 var/let)

    打印地址: withUnsafePointer(to:)函数来打印值类型变量的内存地址

    withUnsafePointer(to: &coordA) { print("\($0)") }

    withUnsafePointer(to: &coordB) { print("\($0)") }

    // 0x000000011df6ec10

    // 0x000000011df6ec20

    在 Swift 中,双等号(== & !=)可以用来比较变量存储的内容是否一致,如果要让我们的 struct 类型支持该符号,则必须遵守 Equatable 协议

    引用类型:

    class、闭包、函数

    引用类型,即所有实例共享一份数据拷贝。

    引用类型的赋值是浅拷贝(Shallow Copy),引用语义(Reference Semantics)即新对象和源对象的变量名不同,但其引用(指向的内存空间)是一样的,因此当使用新对象操作其内部数据时,源对象的内部数据也会受到影响

    如果声明一个引用类型的常量,那么就意味着该常量的引用不能改变(即不能被同类型变量赋值),但指向的内存中所存储的变量是可以改变的

    引用类型打印地址值:

    print(Unmanaged.passUnretained(dogA).toOpaque())

    print(Unmanaged.passUnretained(dogB).toOpaque())

    // 0x0000600000031380

    // 0x0000600000031380

    swift的===和==:

    三等号(=== & !==)可以用来比较引用类型的引用(即指向的内存地址)是否一致。也可以在遵守 Equatable 协议后,使用双等号(== & !=)用来比较变量的内容是否一致

    swift的===和==:https://www.jianshu.com/p/b1df95cb286c

    ==:默认比较基本类型的值,Int String;不能比较引用类型或值类型,除非引用类型或值类型实现了Equatable协议

    比较变量值,不比较指针是否指向同一内存

    ===:检查两个对象是否完全一致,只能比较引用类型,不能比较基本类型和值类型

    不仅比较变量值,还会比较指针是否指向同一内存

    ==比较两个对象即使返回true这两个对象也不一定是完全相同的,可能只是对象的属性值相同,而===返回true则证明比较的对象是完全一致的

    oc的==和isEqual:

    oc的==判断的是地址值;

    oc的isEqual判断的两个对象的值是否相等;

     

     

     

     

    1>值类型和引用类型函数传参的不同:

    函数的参数默认为常量,即在函数体内只能访问参数,而不能修改参数值,具体来说:

    2>值类型作为参数传入时,函数体内部不能修改其值

    引用类型作为参数传入时,函数体内部不能修改其指向的内存地址,但是可以修改其内部的变量值

     

    值类型和引用类型对比:

    1》引用类型必须明确指定init,不指定编译器报错

    2》值类型赋值给新对象时,新对象将赋值;引用类型赋值给新对象时,新旧指向同一个对象

    3》引用类型的默认值是可以修改的,而值类型的默认值是只读的。值类型要修改必须用mutating来修饰,class中则不同,可以直接给self赋值。

     

    关于Swift中Struct,Class和Enum的那些事儿https://mp.weixin.qq.com/s/Z5txegks7-he2hhSj_DRbg

    {//引用类型必须明确指定init

    //由于class之间可以存在继承关系,因此它的初始化过程要比struct复杂,为了保证一个class中的所有属性都被初始化,Swift中引入一系列特定规则

            

    // 一、指定构造器

    //        class Point2D0{//这样写报错 Class 'Point2D0' has no initializers

    //            var x: Double

    //            var y: Double

    //        }

            //1>默认构造器,系统自带的init

            class PointDefaultClass{

                var x: Double = 0.0

                var y: Double = 0.0

            }

            let design = PointDefaultClass();

            

            //2>         memberwise init方法 自定义init后,默认的init消失

            class PointMemberwiseClass{

                var x : Double//非可选类型属性,要么初始化的时候给值,要么在构造方法里给值

                var y : Double

                init(x:Double,y:Double) {

                    self.x = x

                    self.y = y

                }

            }

            //如果给了memberwise构造器,即重写了init方法,则指定构造器let design = PointDesignedClass();失效;会导致编译错误。因为,我们接手了init的定义后,编译就不会插手init工作。所以,在定义init方法时添加默认参数, 我们称这种初始化为 designated init

            let memberwise = PointMemberwiseClass(x: 2, y: 3)

            

            //3> designed init:即在定义init方法时,添加默认参数(不论属性声明时是否赋值),此时系统init的方法又出现了

                class PointDesignedClass{

    //                    var x: Double = 0.0

    //                    var y: Double = 0.0

                    var x: Double

                    var y: Double

                    init(x:Double=1,y:Double=0) {

                            self.x = x

                            self.y = y

                    }

                }

            let designed1 = PointDesignedClass()

            print(designed1.x)

            let designed2 = PointDesignedClass(x: 2, y: 3)

            print(designed2.x)

     

    // 二、便利构造器 convenience 需要用convenience修饰的init方法

    //    必须调用指定构造器完成对象初始化,直接调self.x self.y会导致编译错误

            class ConvienceClass{

                var x : Double

                var y : Double

                init(x : Double = 0, y: Double = 0) {

                    self.x = x

                    self.y = y

                }

                convenience init(at:(Double,Double)){

                    self.init(x: at.0,y: at.1)

                }

            }

    //三、可失败构造器 ??

    //        class failClass{

    //            convenience init?(at: (String,String)){

    //                guard let x = Double(at.0 ),let y = Double(at.1 ) else {

    //                    return nil

    //                }

    //                self.init(at:(x,y))

    //           }

    //        }

            

    //   总结:指定构造器必须调用其直接父类的指定构造器;

    //             便利构造器必须调用同类中定义的其它构造器;

    //             便利构造器必须最终导致一个指定构造器被调用

    //        指定构造器必须总是向上代理;便利构造器必须总是横向代理

    //        如果派生类没有定义任何designated initializer,那么它将自动继承所有基类的designated initializer

    //        如果一个派生类定义了所有基类的designated init,那么它将自动继承基类所有的convenience init

            

      

            /**

            init的继承

              1、如果派生类没有定义任何designated initializer,那么它将自动继承所有基类的designated initializer

              2、如果一个派生类定义了所有基类的designated init,那么它将自动继承基类所有的convenience init

             */

            /**

             init的重载

             在派生类自定义designated init, 表示明确控制派生类的初始化构造过程, Swift 就不会干涉构造过程。那么,之前创建Point3D就会出现错误

             ***如果想让Point3D从Point2D继承所有的convenience init,只有在派生类中实现所有的designated init方法

              

             */

    //        class Point3D: Point2D {

    //            var z: Double

    //            init(x: Double = 0, y: Double = 0, z: Double = 0) {

    //                self.z = z

    //                super.init(x: x, y: y)

    //            }

    //            override init(x: Double, y: Double) {

    //                // 注意先后顺序

    //                self.z = 0

    //                super.init(x: x, y: y)

    //            }

    //        }

    //        此时,就可以正常工作了。只要派生类拥有基类所有的designated init方法,他就会自动获得所有基类的convenience init方法。另外,重载基类convenience init方法,是不需要override关键字修饰

            

        }

     

    1.8

    1、颜色选择器:#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)

          图片选择器:#imageLiteral(resourceName: "tab_mine_S")

     

    1.10

    setter 和 getter

    Getter和setter默认和原属性访问等级相同,但是swift允许给setter设置比原属性等级低的访问等级,这样可以有效的读写保护。

    语法是在var前写

    fileprivate(set), private(set) , internal(set)(set 可以换成 get)

    例:

    private (set) var view:UIView

    https://www.jianshu.com/p/e6021e4f7482

     

    1.11 IBDesignable 和IBInspectable

    swift:https://medium.com/anantha-krishnan-k-g/ibdesignable-and-ibinspectable-in-swift-3-702d7dd00ca

    @IBDesignable @IBInspectable

    oc:  https://www.jianshu.com/p/a90e44ba1f2b

    IB_DESIGNABLE @property(nonatomic,assign) IBInspectable CGFloat cornerRadius;

     

    1.12

    一、extension:

    1、使用范围:可以给类、结构体、枚举、协议添加新功能,类似于oc的category

    2、作用:

    1》一般可以添加计算属性,但是添加存储属性需要通过runtime;

     1.1》增加计算属性:

      计算属性只能计算

       extension Double{

        var km : Double { return self * 1000.0 }

        var m: Double { return self }

        var cm: Double { return self / 100.0 }

        var mm: Double { return self / 1000.0 }

        var ft: Double { return self / 3.28084 }

       }

       let oneInch = 25.4.mm  (换算成meter) 

     1.2》增加存储属性:runtime

        objc_getAssociatedObject;

        objc_setAssociatedObject

    2》增加方法:

    增加实例方法 类型方法 可变实例方法:(扩展增加的实例方法可以修改实例本身。结构体和枚举类型中的方法如果想要修改实例本身或者属性的话需要用mutating来修饰方法,所以扩展这样的方法也需要加mutating)

     

    3》提供新的初始化器:

      增加构造方法:

     只能增加遍历构造方法,不能增加指定构造方法

    4》定义下标:扩展可以给存在的类型增加新的下标

       extension Int{

       subscript(digitIndex: Int) -> Int {

         var decimalBase = 1

         for _ in 0..<digitIndex {

           decimalBase *= 10

         }

         return(self/ decimalBase) % 10

        }

       }

       746381295[0] // returns 5

       746381295[1] // returns 9

       746381295[2] // returns 2

       746381295[8] // returns 7

    5》给类、结构体、枚举 定义和使用新的内置类型:

    6》让一个存在的类型服从一个协议:

     

    二、存储属性和计算属性比较:https://www.jianshu.com/p/986ae3d8bee4

    1、计算属性可以用于 类+结构体+枚举;存储属性可以用于类+结构体

    2、存储属性可以是变量存储属性(var);也可以是常量存储属性(let)

       计算属性只能是var

    3、计算属性不能直接存储,而是提供一个get和set方法,间接设置或获取其它属性

     

    三、UIVisualEffectView 模糊效果

    高斯模糊:https://www.jianshu.com/p/90cdbc56c06d

     

    1.12

    1、edgesForExtendedLayout  https://www.jianshu.com/p/ca3c5a94c32b

    通过设置此属性,你可以指定view的边(上、下、左、右)延伸到整个屏幕

    2、  https://blog.csdn.net/shifang07/article/details/76204378

          1》swift枚举的原始值rawvalue

          enum WeekDayWithRaw : String {

              case Monday = "1. Monday"

              case Tuesday = "2. Tuesday"

              case Wednesday = "3. Wednesday"

              case Thursday = "4. Thursday"

              case Friday = "5. Friday"

              case Saturday = "6. Saturday"

              case Sunday = "7. Sunday"

           }

            let day = WeekDayWithRaw(rawValue: "3. Wednesday")

          2》swift的关联值: 

            enum Shape {

                case Rectangle(CGRect)

                case Circle(CGPoint,Int)

             }

     

    1.13 

    1》swift关键字

    https://mp.weixin.qq.com/s/AeOStA2MkuBW-c_Bhe0xhA

     

    2》compactMap

    过滤数组,获得新的数组;或对数组元素处理获取新的数组(可以是元素类型不同的数组)

    https://blog.csdn.net/yao1500/article/details/80407358

     

    3》遵守NSCoding协议的类的子类都要实现 required init方法

        required init?(coder aDecoder: NSCoder) {

            fatalError("init(coder:) has not been implemented")

        }

     

    https://segmentfault.com/q/1010000005792852

    是NSCoding协议定义的方法,是必要的构造器,当没有任何指定构造器时,会默认继承父类的必要构造器;当在子类中定义或重写指定构造器就必须实现父类的指定构造器;如果在子类中只定义了便利构造器则必要构造器会自动继承。

    UIViewController 的那个无参的构造器是从 UIKit 里面迁移过来的,也就是说这个无参的构造器实际上就是 Objective-C 的 UIViewController 的无参构造方法,因为UIKit是OC写的,是Objective-C bridge过来的。

    convenience构造器可以调用init(string: String)但不能调用init?(coder aDecoder: NSCoder)吗?若是这样,那是应为init?(coder aDecoder: NSCoder)是可失败构造器,要么你把你的那个构造器也弄成可失败,要么直接用‘!’解开optional。

    NSCoding协议的init?(coder aDecoder: NSCoder)为么是必要构造器

     

    xib的初始化要调用initWithCoder原因: https://www.jianshu.com/p/c6bfe153775a

    .nib文件,是程序员通过Interface Builder(界面生成器)构建界面之后生成的包括界面,按钮点击事件等内容的归档(archiving)文件。它可以被程序员直接使用,但是使用前需要进行解除归档(Unarchiving)操作

     

    4》swift的set和get方法:https://www.jianshu.com/p/87fc570e44df

    Get方法里:

    var c:Int{

      get{

         //不能写 c; self.c; _c; 都会死循环  

      }

      set{

         //不能写 c; self.c; _c; 都会死循环

      }

    }

     

    1.14、

    学习flutter网站 https://flutterchina.club/flutter-for-ios/

     

    1.15、

    1》UnsafeMutableRawPointer  UnsafeRawPointer

     

    private let parallaxHeaderKVOContext = UnsafeMutableRawPointer.allocate(

        byteCount: 4,

        alignment: 1

    )

     

    swift的指针:

    Pointer结尾的类型一般都是swift类型的指针;https://www.jianshu.com/p/103591adc3d1

    iOS中有的库还是使用C语言构建的,在与C混编的时候,不可避免的要将对象转换为C的指针类型以便传值。比如GCD,CF框架Runloop操作;

    swift与c指针转换对应如下:

    1.1》返回值、变量、参数指针:

    const int *   ===》    UnsafePointer <Int32>

    int *  ===》   UnsafeMutablePointer <Int32>

    1.2》类对象指针:

    Type * const *    ===》    UnsafePointer<Type>

    Type * __strong * === 》   UnsafeMutablePointer<Type>

    Type **  ===》   AutoreleasingUnsafeMutablePointer<Type>

    1.3》无符号类型:

    const void *   ===》   UnsafeRawPointer

    void *  ===》  UnsafeMutableRawPointer

    如果像不完整结构体的这样的c指针的值的类型无法用Swift来表示,则用OpaquePointer来表示

    1.2》常量指针:

    UnsafePointer<Type>  c中有const修饰

    1.2.1》类型为UnsafePointer<Type>, UnsafeMutablePointer<Type>, or AutoreleasingUnsafeMutablePointer<Type>或者转换为UnsafePointer<Type>类型的值

    1.2.2》String类型的值,如果类型是Int8或UInt8,那字符串会自动转换为UTF8的buffer,然后这个buffer的指针将会传入函数

    1.2.3》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这样的情况下,将左边起始地址传入函数

    1.2.4》[Type]数组类型值,将数组起始地址传入函数。

    1.3》可变指针:定义为UnsafeMutablePointer<Type>、UnsafeMutableRawPointer的指针为可变指针

    1.3.1》类型为UnsafeMutablePointer<Type>类型的值

    1.3.2》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这样的情况下,将地址传入函数

    1.3.3》表达式[Type]类型的变量、属性、同类型的下标表达,这样的情况下,将左边起始地址传入函数

    1.3》自动释放指针:Autoreleasing Pointers

    定义为AutoreleasingUnsafeMutablePointer<Type>,可以接收下面几种类型的值:

    1.3.1》AutoreleasingUnsafeMutablePointer<Type>的值

    1.3.2》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这里会按位拷贝到临时缓冲区,缓冲区的值会被加载,retain,分配值  注意:不能传数组

    1.4》函数指针:在C中有回调函数,当swift要调用C中这类函数时,可以使用函数指针

    swift中可以用@convention 修饰一个闭包:

    @convention(swift) : 表明这个是一个swift的闭包;

    @convention(block) :表明这个是一个兼容oc的block的闭包,可以传入OC的方法

    @convention(c) : 表明这个是兼容c的函数指针的闭包,可以传入C的方法

    (C中的方法int (*)(void) 在swift中就是@convention(c) () -> Int32)

    1.5》Buffer指针:buffer指针用于比较底层的内存操作,你可以使用buffer指针做高效的处理和应用程序与服务间的通信。有下面几种类型的buffer指针:

    UnsafeBufferPointer;

    UnsafeMutableBufferPointer;

    UnsafeRawBufferPointer;

    UnsafeMutableRawBufferPointer;

    UnsafeBufferPointer、 UnsafeMutableBufferPointer,能让你查看或更改一个连续的内存块;UnsafeRawBufferPointer、UnsafeMutableRawBufferPointer能让你查看或更改一个连续内存块的集合,集合里面每个值对应一个字节的内存

     

    1.6》指针用法:

    当使用指针实例的时候,可以使用pointee属性获取指针指向内容的值,指针不会自动管理内存或对准担保。你必须自己管理生命周期以避免泄露和未定义的行为

    内存可能有几种状态:

    未分配的:没有预留的内存分配给指针;已分配的:指针指向一个有效的已分配的内存地址,但是值没有被初始化;已初始化:指针指向已分配和已初始化的内存地址 指针将根据我们具体的操作在这 3 个状态之间进行转换。

    用法示例:

    未分配的指针用allocate方法分配一定的内存空间;

    分配完内存空间的指针用各种init方法来绑定一个值或一系列值。初始化时,必须保证指针是未初始化的。(初始化过的指针可以再次调用初始化方法不会报错,所以使用时需要特别注意。);

    然后修改值

    回到初始化值之前,没有释放指针指向的内存,指针依旧指向之前的值。

    释放指针指向的内存,据官方文档说,在释放指针内存之前,必须要保证指针是未初始化的,不然会产生问题。

    1.7》

    转换:指针间转换

    可变指针与不可变指针间转换

    1.7.1》一个函数需要传入不可变指针时,可变指针可以直接传入

    1.7.2》一个函数需要可变指针时,可以使用init(mutating other: UnsafePointer<Pointee>)方法转换

    var i: Int8 = 12

    func printPointer(p: UnsafePointer<Int8>) {

        let muS2ptr = UnsafeMutablePointer<Int8>.init(mutating: p)!  //  UnsafePointer<Int8> -> UnsafeMutablePointer<Int8>

        print(muS2ptr.pointee)

        

        var constUnTypePointer = UnsafeRawPointer(p) // UnsafePointer<Int8> - > UnsafeRawPointer

        var unTypePointer = UnsafeMutableRawPointer(mutating: constUnTypePointer) // UnsafeRawPointer -> UnsafeMutableRawPointer

        var unTypePointer2 = UnsafeMutableRawPointer(muS2ptr) // UnsafeMutablePointer<Int8> ->  UnsafeMutableRawPointer

        

    }

    转换:不同类型指针间转换

    1.7.3》 Uint8的指针 转换为UInt64

    let pointer1 = UnsafeRawPointer(uint8Pointer).assumingMemoryBound(to: UInt64.self)

    let pointer2Value = UnsafeRawPointer(uint8Pointer).load(as: UInt64.self)

    let pointer3 = UnsafeMutablePointer(mutating: uint8Pointer).withMemoryRebound(to: UInt64.self, capacity: 1) { return $0 }

     

    转换:指针转换成swift

    1.7.4》load等方法转为对应的swift类型

    func print<T>(address p: UnsafeRawPointer, as type: T.Type) {

        let value = p.load(as: type)

        print(value)

    }

    转换:对象转换

    1.7.5》在调用一些底层库的时候,经常要把Swift对象传入C中,然后将从C中的回调函数中转换成Swift对象

    C函数的回调函数中,传出来一个UnsafeMutableRawPointer对象的指针,我展示了3种方式,可以将这个指针转换为ViewController对象:

    let controllerPoint = withUnsafeMutablePointer(to: &blockSelf) { return $0}

    var context = Context(info: controllerPoint, retain: nil)

    abcPrint(&context) {

                let controller1 = $0?.assumingMemoryBound(to: ViewController.self).pointee            

                let controller2 = $0?.bindMemory(to: ViewController.self, capacity: 1).pointee            

                let controller3 = $0?.load(as: ViewController.self)

    }

     

    1.8、CFRoopLoop使用

    Unmanaged方式:

    苹果的一些底层框架返回的对象有的是自动管理内存的(annotated APIs),有的是不自动管理内存

    对于Core Fundation中有@annotated注释的函数来说,返回的是托管对象,无需自己管理内存,可以直接获取到CF对象,并且可以无缝转化(toll free bridging)成Fundation对象,比如NSString和CFString。目前,内存管理注释正在一步步的完善,所以等到未来某一个版本我们就可以完完全全的像使用Fundation一样使用Core Fundation

     

     

     

    2》NSKeyValueObserving   swift的kvo

     

          guard let scrollView = self.superview as? UIScrollView else {

                return

            }

            scrollView.addObserver(

                self.parent,

                forKeyPath: NSStringFromSelector(

                    #selector(getter: scrollView.contentOffset)

                ),

                options: NSKeyValueObservingOptions.new,

                context: parallaxHeaderKVOContext

            )

     

    kvo contentOffset 

     

    kvo:

    webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)

     override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

           if keyPath == "estimatedProgress" {

               progressView.isHidden = webView.estimatedProgress >= 1

               progressView.setProgress(Float(webView.estimatedProgress), animated: true)

           }

       }

       

     

    1.16 

    1》约束详解: https://www.jianshu.com/p/d67395deb694

    translatesAutoresizingMaskIntoConstraints

    translatesAutoresizingMaskIntoConstraints 的本意是将 frame 布局 自动转化为 约束布局,转化的结果是为这个视图自动添加所有需要的约束,如果我们这时给视图添加自己创建的约束就一定会约束冲突。

    为了避免上面说的约束冲突,我们在代码创建 约束布局 的控件时 直接指定这个视图不能用frame 布局(即translatesAutoresizingMaskIntoConstraints=NO),可以放心的去使用约束了。

    2》

     

     

    http://app.u17.com/v3/appV3_3/ios/phone/comment/list

     

     

    1.19

    1、字符串拼接方法:\()\()

    textView.text = "【\(model.comic?.cate_id ?? "")】\(model.comic?.description ?? "")"

    2、swift的cell中设置model(oc中cell的setModel方法)在model的didSet中方法设置:

    var model: DetailStaticModel? {

            didSet{

                guard let model = model else { return }

                textView.text = "【\(model.comic?.cate_id ?? "")】\(model.comic?.description ?? "")"

           }

    }

     

    1.30:

    @inlinable 

    这些对于应用程序不是必须的,开发者可以将一些公共功能注释为@inlinable,这给编译器提供了优化跨模块边界的泛型代码的选项。

     

     

    2.1 

    1、swift循环引用: https://www.jianshu.com/p/f9bc8afd5b53

    1》强引用,弱引用

    2》循环引用和内存泄漏:

       2.1》变量间互相引用 引起的泄露   

        引用类型间的互相引用会出现循环引用;因为引用或传递引用类型的变量时不会在内存中重新分配地址;像struct之间互相引用就不会发生循环引用;

       值类型间的互相引用不会出现循环引用;因为引用或传递值类型的变量时会在内存中重新分配地址;像class之间互相引用就会发生循环引用;

       避免方法:使用weak

       2.2》Protocol(委托代理模式中) 引起的泄露

       委托代理模式中class、struct、enum都可以用Protocol,但是只有class时才会引起循环引用,所以,只有在class中使用Protocol才需要weak修饰delegate属性,因此,声明协议时,指定只有class类型的变量可以代理它(

    即:protocol ListViewControllerDelegate : class {

        func configure(with list : [Any])

    }

       避免方法:使用weak 和 class

     

       2.3》Closure 引起的泄露:(对象对closure有一个强引用,同时在closure的代码块中又对该对象本身有一个强引用。这样就引起了循环引用的发生)

        

       避免方法:使用[unowned self] 和 [weak self]

       使用[unowned self] 的时候需要注意的一点是:调用closure的时候如果对象已经被释放的话,会出现crash;

       [weak self] 和[unowned self] 的区别是 [weak self]处理的时候是一个可选类型。

     

    2、Date()

     

    2.2

    设置tableview的tableView:heightForHeaderInSection 或 tableView:heightForFooterInSection的高度为0:

    以前设置为0.01会导致像素偏移,最佳方法是设置为float中的最小非零数字 

    oc是FLT_TRUE_MIN ;swift CGFloat.leastNonzeroMagnitude 表示CGFloat支持的最小非零十进制数值

    (注:oc是FLT_MIN swift是CGFloat.leastNormal;Magnitude 表示CGFloat支持的最数值,包括零)

    https://www.jianshu.com/p/103b79891e8b

     

    展开全文
  • swift基本语法

    千次阅读 2016-04-26 15:43:16
    可选类型知识点补充1 例 : 强转并且制定类型let str = "23" //打印结果: "23" let age : Int? = Int(str) //打印结果 :23—-> 1.1 问题 : 该例子转化的结果为什么需要用可选类型来接收?—-> 1.2 解答 : 因为很...

    一 可选类型知识点补充

    1 例一 : 强转并且制定类型

    let str = "23"  //打印结果: "23"
    let age : Int? = Int(str) //打印结果 :23
    —-> 1.1 问题 : 该例子转化的结果为什么需要用可选类型来接收?
    —-> 1.2 解答 : 因为很有可能是转化不成功的,如果此时不用可选类型来接收的话,万一转化失败,那么该值就为空(nil),如果不选择可选类型来接收,程序会崩溃.

    2 例二 : 获取某个plist文件的路径

    let path : String? = NSBundle.mainBundle().pathForResource("xiaofeng.plist", ofType: nil) //打印的结果为nil,因为写程序的时候并没有新建一个名字为"xiaofeng"的plist文件
    if let path = path {
        NSArray(contentsOfFile: path)
    }
    —-> 2.1 这里同样需要用可选类型来接收,原因是很有可能找不到名字为”xiaofeng”的plist文件,如果此时不用可选类型来接收,值为nil,程序会崩溃.
    —-> 2.2 在此句后面写一个判断语句更加安全的判断是否可以取成功.判断的好处如下:
    —> 2.2.1 判断path是否有值,如果没有值的话,就不执行{}中的内容
    —> 2.2.2 如果path有值,那么系统就会对path进行解包,并且将解包的结果赋值

    3 例三 : 获取一个NSURL

    let url : NSURL? = NSURL(string: "www.baidu.com") //打印结果:www.baidu.com
    if let url = url {
        NSURLRequest(URL : url) //<NSURLRequest: 0x7fd050d18610> { URL: www.baidu.com }
    }
    —-> 3.1 如果不加判断或者将获取到的值不用可选类型来接收的话,那么当url是下面这种类型,就获取不到.不用选类型来接收是会报错的.
    //url中出现了中文,并且没有用可选类型来接收值,系统会报错
    //系统包的错误: cannot convert value of type 'NSURL?' to specified type 'String'
    let url : String = NSURL(string: "www.baidu.com/百度")
    —-> 3.2 如果用if做出了判断,此时能更加安全的对url进行操作,判断url是否获取成功,成功的话就将url解包.

    4 例三 : 在保证一个可选类型一定有值的前提下,可以直接强制解包

    —-> 4.1 如果此时的123.plist文件存在的话,就可以强制解包(通过按住option点击path1可以看出,path1的类型是可选类型)
    let path1 = NSBundle.mainBundle().pathForResource("123.plist", ofType: nil)

    二 is; as?; as!的用法

    1 is : 用来判断某种类型是不是另外一种类型(用法比较简单)

    let array = [12,"xiaofeng",1.89]
    let arrayM = [NSObject]()//定义数组,内部的元素是NSObject类型
    //判断取出的第一个元素是不是整型
    if let firstObject = array.first {
        if firstObject is Int {
            print("是Int类型")    //打印结果:"是Int类型\n"
        }else{
            print("不是Int类型")
        }
    }

    2 as? : 转成的最终类型是一个可选类型(需要作出判断)

    let dict = ["name" : "xiaofeng", "age" : 20, "height" : 1.88]
    let value = dict["name"] //通过按住option点击value,可以看出类型为可选类型
    //将NSObject转成String类型
    if let value = value {
        //此处value as? String也有可能转失败
        let valueStr : String? = value as? String
        //判断是否能转成功
        if let valueStr = valueStr {
            let info = "my name is " + valueStr //最终结果:"my name is xiaofeng"
        }
    }
    —-> 2.1 简便写法判断NSObject? 转 String
    if let valueStr = value as? String {
        let info = "my name is " + valueStr //打印结果 :"my name is xiaofeng"
    }

    3 as! : 转成的最终类型就是具体的类型(NSObject? 转成 String)

    —-> 3.1 此方法的后果: 如果用这种方法转化不成功,那么程序会直接崩溃
    let info = "my name is " + (value as! String) //打印结果 :"my name is xiaofeng"

    三 swift中的函数

    1 oc中的方法

    有参数有返回值
    - (CGFloat)sum:(CGFloat)num1 num2:(CGFloat)num2;
    有参数没有返回值
    - (void)sum1:(CGFloat)num1 num2:(CGFloat)num2;
    没有参数有返回值
    - (CGFloat)sum2;
    没有参数没有返回值
    - (void)sum3;

    2 swift中的函数相当于oc中的方法(格式如下)

    func 函数名(参数列表) -> 返回值类型 {
        代码块
        return 返回值
    }
    —-> 2.1 func是关键字,多个参数列表之间可以用逗号(,)分隔,也可以没有参数
    —-> 2.2 使用箭头“->”指向返回值类型
    —-> 2.3 如果函数没有返回值,返回值为Void.并且“-> 返回值类型”部分可以省略

    3 常见的函数类型

    —-> 3.1 有参数有返回值
    func sum (num1 : Int , num2 : Int) ->Int {
        return num1 + num2
    }
    let result = sum(10, num2: 10)
    print("计算的结果是:\(result)")  //打印结果 : "计算的结果是:20\n"
    —-> 3.2 有参数没有返回值
    func minus (num1 : Double , num2 : Double) {
        print(num1 - num2)   //打印出来的结果 : "2.0\n"
    }
    minus(22.1, num2: 20.1)
    —-> 3.3 没有参数有返回值
    func multiply() ->Int {
        return 10   //返回结果 : 10
    }
    multiply()
    —-> 3.4 没有参数没有返回值
    func divide() {
    }
    divide()
    —-> 3.5 有参数有多个返回值(元组)
    —-3.5.1 计算一个数组中奇数和偶数的个数?
    * 普通写法:
    let array = [11,22,44,33,66,88]
    var oddCount = 0 //奇数
    var evenCount = 0 //偶数
    for num in array {
        if num % 2 == 0 {
            evenCount++  //打印结果 : 4
        } else {
            oddCount++  //打印结果 : 2
        }
    }
    * 利用元组特性作为多个返回值
    let nums = [11,22,33,44,55,7,77,88]
    
    func getCount (nums : [Int]) ->(Int,Int) {
        var evenCount = 0 //偶数
        var oddCount = 0 //奇数
        for num in nums {
            if num % 2 == 0 {
                evenCount++    //打印结果 : 3
            }else {
                oddCount++    //打印结果 : 5
            }
        }
        return (evenCount,oddCount)
    }
    let count = getCount(nums)
    print("偶数的个数是:\(count.0),奇数的个数是:\(count.1)")

    4 内部参数和外部参数

    —-> 4.1 含义: 内部参数: 在函数内部可以看到的参数,就是内部参数;外部参数: 在函数外面可以看到的参数,就是外部参数
    func sum (num1 : Int , num2 : Int , num3 : Int) ->Int {
        print(num1)
        print(num2)
        print(num3)
        return num1 + num2 + num3   //返回结果 : 30
    }
    //调用
    sum(10, num2: 10, num3: 10)
    —-> 4.2 为什么上面代码在调用的时候,num1并没有显示出来,而是从num2开始显示的?
    —-> 4.3 解答 : 默认情况下,从第二个参数开始,参数名称既是内部参数也是外部参数
    —-> 4.4 如何让第一个参数也是外部参数?
    —-> 4.5 解答 : 可以在第一个标识符前加上和该标识符同名的标识符
    func multiply (num1 num1 : Int , num2 : Int , num3 : Int) ->Int {
        return num1 * num2 * num3  //返回结果 : 1000
    }
    multiply(num1: 10, num2: 10, num3: 10)
    —-> 4.6 如何让外部参数不显示?
    —-> 4.7 可以在参数名称前加下划线(_)
    func subtraction (num1 : Int , _ num2 : Int , _ num3 : Int) ->Int {
        return num1 - num2 - num3   //返回结果 : 50
    }
    subtraction(100, 20, 30)

    5 默认参数

    —-> 5.1 含义 : 某些情况,如果没有传入具体的参数,可以使用默认参数(“蓝山”)
    —-> 5.2 需求 : 刚进入公司作为新员工,老员工需要你帮他们泡咖啡,他们说了各自的咖啡需求,但是有一些人没有说,只说了随便,此时要你设计一个函数求出咖啡的种类?
    func makeCoffee(coffeeName : String = "蓝山") ->String {
        return "制作了一杯:\(coffeeName)咖啡"
    }
    makeCoffee("拿铁")  //通过传入咖啡的名称,返回制作好的咖啡
    makeCoffee()       //此时的调用函数,返回的结果是默认设置的"蓝山"咖啡

    6 可变参数

    —-> 6.1 含义与条件
    —–> 6.1.1 swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数
    —–> 6.1.2 它们必须具有相同的类型
    —–> 6.1.3 我们可以通过在参数类型名后面加入(…)的方式来指示这是可变参数
    —-> 6.2 需求 : 第一天 老板要你通过函数计算两个数的相加;第二天 老板要你通过函数计算三个数的相加; 第三天 老板要你通过函数计算四个数的相加
    //此时需要些三个方法来计算
    func sum(num1 : Int , num2 : Int) ->Int {
        return num1 + num2
    }
    func sum(num1 : Int , num2 : Int , num3 : Int) ->Int {
        return num1 + num2 + num3
    }
    func sum(num1 : Int , num2 : Int , num3 : Int , num4 : Int) ->Int {
        return num1 + num2 + num3 + num4
    }
    sum(10, num2: 20, num3: 30, num4: 40)
    —-> 6.3 使用可变参数进行改进
    //计算参数为数组中的各个元素的和(方法一)
    func arrayCount(nums : [Int]) ->Int {
        var result = 0
        for num in nums {
            result += num
        }
        return result
    }
    arrayCount([10,20,30,40])  //传入一个数组   打印结果是 : 100
    //计算参数为数组中的各个元素的和(方法二)
    func sunNums(nums : Int...) ->Int {
        var result = 0
        for num in nums {
            result += num
        }
        return result
    }
    sunNums(10,20,30,40)

    7 引用类型(指针的传递)

    —-> 7.1 默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址
    —-> 7.2 必须是变量,因为需要在内部改变其值
    —-> 7.3 Swift提供的inout关键字就可以实现
    —-> 7.4 需求 : 用函数交换两个数的值
    —–> 7.4.1 错误写法 :
    var m = 20
    var n = 30
    func exchangeNum(var num1 : Int , var num2 : Int) {
        let tempNum = num1
        num1 = num2
        num2 = tempNum
    }
    exchangeNum(m, num2: n)
    print("m :\(m), n: \(n)")    //打印出结果 : "m :20, n: 30\n"
    —–> 7.4.2 正确写法 一: 运用指针进行交换(swift提供的关键字: inout)
    var a = 20
    var b = 30
    func exchangeNum(inout num1 : Int , inout num2 : Int) {
        let tempNum = num1
        num1 = num2
        num2 = tempNum
    }
    exchangeNum(&a, num2: &b)
    print("a : \(a) , b : \(b)")     //打印结果 : "a : 30 , b : 20\n"
    —–> 7.4.3 特别写法 :
    var c = 20
    var d = 30
    func exchangeNum( num1 : Int , num2 : Int) -> (Int , Int) {
        return (num2 ,num1)
    }
    let num = exchangeNum(c, num2: d)
    c = num.0     //打印结果 : 30
    d = num.1     //打印结果 : 20

    8 函数的嵌套使用

    —-> 8.1 在swift中函数是可以嵌套使用的(不推荐)
    func test() {
        func demo() {
            print("demo")
        }
        demo()    //先打印:demo
        print("test")  //再打印:test
    //    demo()    //如果在此处调用   先打印:test  再打印:demo
    }
    test()

    9 函数的类型

    —-> 9.1 定义两个函数
    func add(a : Int , b : Int) ->Int {
        return a + b
    }
    func mul(a : Int , b : Int) ->Int {
        return a * b
    }
    —-> 9.2 写出函数的类型 (函数相当于一个值)
    var a = add    //打印出函数的类型 : (Int, Int) -> Int
    var a : (Int, Int) -> Int = add
    a(20,30)    //打印结果 : 50
    a = mul     //打印结果 : (Int, Int) -> Int
    a(30,60)    //打印结果 : 1800
    —-> 9.3 函数作为方法的参数
    —–> 9.3.1 定义一个函数
    var m : Int = 20
    func test(num : Int) ->Int {
        return num
    }
    test(m)   //打印结果 : 20
    test(30)  //打印结果 : 30
    —–> 9.3.2 用第一个函数作为第二个函数的参数
    func demo(funcName : (Int) ->Int) {
        funcName(20)
    }
    demo(test)
    —–> 9.3.3 函数作为方法的返回值
    func add(a : Int , b : Int) ->Int {
        return a + b
    }
    func demo1() ->((Int, Int) ->Int) {//返回值的类型必须是和返回函数同种类型
        return add
    }
    let tempFunc = demo1()
    tempFunc(20,30)

    四 枚举

    1 OC中的枚举 : 指定相关名称为一组整型值

    typedef enum {
        SexMan,  //默认为0
        SexWoMan //1
    }Sex

    2 swift中的枚举 : Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值.也可以提供一个值是字符串,一个字符,或是一个整型值或浮点值.

    3 swift枚举的语法

    enum SomeEnumeration {
    // 属性(enumeration definition goes here)
    }

    4 定义swift中的枚举(并没有规定Man,WoMan的数值)

    //单个值一行
    enum Sex {
        case Man
        case WoMan
    }
    //多个值一行
    enum Direction {
        case East,West,South,North
    }

    5 创建枚举具体类型

    var sex = Sex.Man
    sex = .WoMan

    6 给枚举类型赋具体值

    enum Planet : Int {
        case Mercury = 0, venus, Earth, Mars
    }
    enum Sex1 : Int {
        case Man = 0
        case WoMan = 1
    }
    enum Sex2 : String {
        case Man = "男"
        case WoMan = "女"
    }

    7 通过下面的方法来取出枚举中的属性(rawValue:)

    let sex1 = Sex1(rawValue: 0)  //打印出结果 : Man
    let sex2 = Sex1(rawValue: 1)  //打印出结果 : WoMan
    let sex4 = Sex2(rawValue: "男")   //打印出结果 : Man
    let sex5 = Sex2(rawValue: "女")   //打印出结果 : WoMan

    五 结构体

    1 结构体的具体使用

    —-> 1.1 需求 : 判断两个点之间的距离是否大于某个数字?
    —-> 1.2 传统做法
    //1.定义中心点
    let centerX : Double = 100
    let centerY : Double = 100
    //获取比较的点
    let x : Double = 60
    let y : Double = 50
    let x1 : Double = 200
    let y1 : Double = 150
    //定义方法,用来比较两个点
    func inRange(x : Double, y : Double) ->Bool {
        //获取两个点之间的距离
        let disX = x - centerX
        let disY = y - centerY
        //计算第三边的距离
        //pow(disX, 2)代表了disX的2次方;sqrt代表开方根
        let distance = sqrt(pow(disX, 2) + pow(disY, 2))
        //判断是否小于200
        return distance < 200    //打印结果 : true
    }
    inRange(60, y: 50)
    —-> 1.3 使用结构体进行该进(优点) : 看上去比较直观,一眼就知道代表了点
    //结构体定义
    struct Location {
        var x : Double
        var y : Double
    }
    //通过结构体创建对应的点
    let center = Location(x: 100, y: 100)
    //需要判断的点
    let testLocation = Location(x: 50, y: 40)
    //定义函数
    func inRang(location : Location) ->Bool {
        let disX = location.x - center.x
        let disY = location.y - center.y
    
        let distance = sqrt(pow(disX, 2) + pow(disY, 2))
        return distance < 200
    }
    //方法的调用
    inRang(testLocation)

    2 结构体扩充构造函数

    —-> 2.1 swift规范 : 在实例化任何一个类或者结构体时,必须保证类/结构体所有(存储)属性,都必须被初始化.
    —-> 2.2 如果自定义了构造函数,那么会覆盖系统提供的构造函数.如果希望保留原来的构造函数,那么必须明确的将系统提供的构造函数进行重写
    struct Location {
        var x : Double = 0
        var y : Double = 0
        var z : Double = 0
        //init()是系统的构造方法
        init() {
        }
        //自定义构造方法
        init(x : Double, y : Double, z : Double) {
            self.x = x
            self.y = y
            self.z = z
        }
    }
    —-> 2.3 注意一 :如果没有自定义构造方法,是可以敲出下面的方法来
    Location()
    —-> 2.4 注意二 : 自定义了构造方法,同时重写了系统的构造方法是可以同时敲出下面的方法
    Location()  //如果init()构造方法没有被重写,是无法敲出来的
    //创建Location实例
    let location = Location(x: 100, y: 100, z: 100)
    —-> 2.5 注意三 : 系统的结构体除了可以通过make方法创建外,也可以直接结构体名称后面跟上()来创建
    let point = CGPoint(x: 100, y: 100)
    let size = CGSize(width: 100, height: 100)
    let rect = CGRect(origin: point, size: size)
    var rect1 = CGRect().origin.x
    rect1 = 100
    let rect2 = CGRect(x: 0, y: 0, width: 100, height: 100)
    let range = NSRange(location: 3, length: 9)

    3 结构体扩充函数

    —-> 3.1 给结构体扩充方法,必须在func前加上Mutating
    —-> 3.2 给自定义的结构体扩充函数
    struct Location {
        var x : Double
        var y : Double
        //mutating少了是会报错的
        mutating func moveH(dis : Double) {
            x += dis
        }
        mutating func moveV(dis : Double) {
            y += dis
        }
    }
    var location = Location(x: 50, y: 60)
    location.moveH(-100)
    location.moveV(100)
    print(location)   //打印出结果 : "Location(x: -50.0, y: 160.0)\n"
    —-> 3.3 给系统的结构体扩充方法
    extension CGPoint {
        mutating func moveH(dis : CGFloat) {
            x += dis
        }
        mutating func moveV(dis : CGFloat) {
            y += dis
        }
    }
    var point = CGPoint(x: 100, y: 100)
    point.moveH(100)
    print(point)   //打印出结果 : "(200.0, 100.0)\n"
    —-> 3.4 给系统的类扩充方法(必须使用系统给的关键字:extension)
    extension UIButton {
        func getTitle() ->String? {
            return titleLabel?.text
        }
    }
    let btn = UIButton()
    btn.setTitle("按钮", forState: .Normal)
    print(btn.getTitle())  //打印出结果 : "Optional("按钮")\n"

    六 类

    1 定义类

    —-> 1.1 类的定义
    class 类名 : SuperClass {
        // 定义属性和方法
    }
    —-> 1.2 注意 : (1)定义的类,可以没有父类.那么该类是rootClass (2)通常情况下,定义类时.继承自NSObject(非OC的NSObject)

    2 类的属性

    —-> 2.1 类的属性种类
    —–> 2.1.1 存储属性:存储实例的常量和变量
    —–> 2.1.2 计算属性:通过某种方式计算出来的属性
    —–> 2.1.3 类属性:与整个类自身相关的属性

    3 存储属性

    class Person: NSObject {
        var name = ""
        var age = 0
        var mathScore = 0.0
        var chineseScroe = 0.0
    }
    //创建Person对象
    let p = Person()
    //给存储属性赋值
    p.name = "xiaofeng"
    p.age = 19
    p.mathScore = 90
    p.chineseScroe = 100

    4 计算属性(本质是一个方法)

    1> 计算属性并不存储实际的值,而是提供一个getter和一个可选的setter来间接获取和设置其它属性
    2> 计算属性一般只提供getter方法
    3> 如果只提供getter,而不提供setter,则该计算属性为只读属性,并且可以省略get{}
    —-> 4.1 需求:计算一个学生的两门课程的平均成绩
    class Person: NSObject {
        var name = ""
        var age = 0
        var mathScore = 0.0
        var chineseScroe = 0.0
        //计算属性(本质是一个方法)
        func getAverageScore() ->Double {
            return (mathScore + chineseScroe) * 0.5
        }
    }
    //创建Person对象
    let p = Person()
    //给存储属性赋值
    p.name = "xiaofeng"
    p.age = 19
    p.mathScore = 90
    p.chineseScroe = 100;
    //调用方法
    print(p.getAverageScore())  //打印结果 : "95.0\n"
    —-> 4.2 计算属性的完整写法
     var averageScore : Double {
            get {
                return (mathScore + chineseScroe) * 0.5
            }
            set (newScore){
                chineseScroe = 2 * newScore - mathScore
            }
        }
    —-> 4.3 计算属性常见写法
     var averageScore : Double {
            return (mathScore + chineseScroe) * 0.5
        }

    5 类属性

    —-> 5.1 类属性是与类相关联的,而不是与类的实例相关联
    —-> 5.2 所有的类和实例都共有一份类属性.因此在某一处修改之后,该类属性就会被修改
    —-> 5.3 类属性的设置和修改,需要通过类来完成
    static var courseCount = 0
    //设置课程个数
    Person.courseCount = 3
    //取出类属性的是
    print(Person.courseCount)

    6 类的构造函数

    —-> 6.1 默认情况下,系统会给类提供一个最简单的构造函数 init()
    —-> 6.2 创建出来一个对象,必须保证该对象内部所有的属性都有初始化值
    —-> 6.3 如果自定义构造函数init(),会覆盖系统默认提供的init()构造函数,如果不希望覆盖,那么必须明确的实现init()构造函数
    class Person {
        var name : String = ""
        var age : Int = 0
        //系统的构造函数
        init() {
        }
        //添加构造函数
        init(name : String, age : Int) {
            self.name = name
            self.age = age
        }
        //用字典添加
        init(dict : [String : NSObject]) {
            if let name = dict["name"] as? String {
                self.name = name
            }
    
            if let age = dict["age"] as? Int {
                self.age = age
            }
        }
    }
    —-> 6.4 创建对象
    //2.创建类对象
    let p = Person()//通过普通方法创建
    //通过扩充的方法创建
    let p1 = Person(dict: ["name" : "xiaofeng", "age" : 19])

    7 类的字典转模型(KVC)

    —-> 7.1 利用KVC字典转模型会更加方便
    —-> 7.2 注意:
    —–> 7.2.1 KVC并不能保证会给所有的属性赋值
    —–> 7.2.2 因此属性需要有默认值
    class Person : NSObject{
        var name : String = ""
        var age : Int = 0
        init(dict :[String : NSObject]) {
            //必须先初始化对象
            super.init()
            setValuesForKeysWithDictionary(dict)
        }
        //如果使用KVC的时候,发现模型属性值并不能一一对应,此时使用kvc是会报错的,但是重写下面这个方法可以避免系统报错
        override func setValue(value: AnyObject?, forUndefinedKey key: String) {
        }
    }
    let p = Person(dict: ["name" : "xiaofeng", "age" : 18])
    print(p.name)    //打印结果 : "xiaofeng\n"
    print(p.age)     //打印结果 : "18\n"

    8 析构函数

    —-> 8.1 Swift 会自动释放不再需要的实例以释放资源,类似于delloc
    —-> 8.2 Swift 通过自动引用计数(ARC)处理实例的内存管理
    —-> 8.3 当引用计数为0时,系统会自动调用析构函数(不可以手动调用)
    —-> 8.4 通常在析构函数中释放一些资源(如移除通知等操作)
    –> 1 析构函数写法
    deinit {
        // 执行析构过程
    }
    –> 2 具体代码
    class Person : NSObject {
        var name = ""
        var age = 0
        //用来释放一些没用的资源,类似于delloc
        deinit {
            print("Person -----deinit")    //打印结果 : "Person -----deinit\n"
            NSNotificationCenter.defaultCenter().removeObserver(self)
        }
    }
    
    var p : Person? = Person()
    p = nil      //此时将p赋值为nil,析构函数中的print会打印Person -----deinit

    9 属性监听

    —-> 9.1 在OC中我们可以重写set方法来监听属性的改变
    —-> 9.2 Swift中可以通过属性观察者来监听和响应属性值的变化
    —-> 9.3 通常是监听存储属性和类属性的改变.(对于计算属性,我们不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化)
    —-> 9.4我们通过设置以下观察方法来定义观察者
    —–> 9.4.1 willSet:在属性值被存储之前设置。此时新属性值作为一个常量参数被传入。该参数名默认为newValue,我们可以自己定义该参数名
    —–> 9.4.2 didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValue
    —–> 9.4.3 willSet与didSet只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法
    class Person: NSObject {
        //属性监听器name
        var name : String = "" {
            //监听属性即将发生改变
            willSet (new) {
            //在该方法中有一个默认的系统属性newValue,用于存储新值
                print(name)    //打印结果 : "\n"
                print(new)     //打印结果 : "xiaofeng\n"
            }
            //监听属性已经发生改变
            didSet (old){
            //在该方法中有一个默认的系统属性oldValue,用于存储旧值
                print(name)     //打印结果 : "xiaofeng\n"
                print(old)      //打印结果 : "\n"
            }
        }
        //属性监听器age
        var age : Int = 0 {
            //在willSet中有newValue,用于保存新值
            willSet (newAge) {
                print(age)
                print(newAge)
            }
            //在didSet中有oldSet,用于保存旧值
            didSet (oldAge) {
                print(age)
                print(oldAge)
            }
        }
    }
    
    //创建对象
    let p = Person()
    p.name = "xiaofeng"
    p.age = 20

    七 总结

    1 该部分接上一篇swift基本语法部分,详细的讲解了函数,自定义构造方法,类部分的相关知识,里面写的并不是很全面,我已经尽可能的完善了,希望能帮到大家.

    2 最后,我还会陆续的更新swift的相关知识,希望看到我博客的开发者,如果觉得我写的还行的话,麻烦大家支持我的博客,关注我的官方博客,谢谢!!!!

    展开全文
  • package com.swift; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAd...
  • (Xcode) 編譯器小白筆記 - LLVM前端Clang 本文为笔记型式呈现,并非全部原创,来源见文末 Compiler ...Apple(包括中后期的NeXT) 一直使用GCC作为官方的编译器。GCC作为开源世界的编译器标准一直做得不错,但...
  • 初始化 反初始化

    2019-07-14 16:30:53
    这个过程需要给实例里的每个存储属性设置个初始值并且在新实例可以使用之前执行任何其他所必须的配置或初始化 2.你通过定义初始化器来实现这个初始化过程,它更像是个用来创建特定类型新实例的特殊的方法。...
  • iOS runLoop 用法详解3

    2016-07-25 12:00:45
    // // ViewController.m // test_runLoop_01 // // Created by jeffasd on 16/7/25. // Copyright © 2016年 jeffasd. All rights reserved. // #import "ViewController.h" ...@interface ViewController () ...
  • 做过iOS动画的朋友都知道,动画中大头疼之处就是弹性、形变之类扭曲的效果。iOS7开始,我们开始可以直接使用UiView的渲染动画API实现简单的弹性效果。 + (void)animateWithDuration:(NSTimeInterval)duration ...
  • 每个虚拟机上至少有两个虚拟磁盘,个用于操作系统安装,另个用于服务GlusterFS存储(sdb)。这将模拟真实世界的部署,您可能希望将GlusterFS存储与操作系统安装分开。 在每个服务器上设置NTP...
  • ()技术篇 问题描述 app进程 基地址:0x0000000102a94000 IDA函数偏移地址:00000001044AF668 so 理论上最终内存地址: 0x102a94000 + 0x1044AF668 = 0x206f43600 断点命令:br s -a 0x206f43600 warning: failed...
  • Advanced+Apple+Debugging(18)

    2019-06-20 18:28:05
    一次汇编代码将会聚焦于如果char不在char的初始位置里--也就是说, 如果这个类还没有被加载的时候这些逻辑做了哪些事情.你需要在紧跟在偏移55的后面的偏移61的汇编指令处位置创建一个断点.你可以随便调用一个类来看...
  • iOS远程推送--APNs详解

    2019-08-27 09:56:11
    iOS远程推送,远远不是配置两个证书,集成个SDK那么简单。 本文会从实践出发,结合苹果的官方文档,带你全面的了解苹果APNs服务。除了基础原理和集成方法外,还将详细介绍了APNs服务接口的调用方式,以及各个推送SDK...
  • 细说GCD

    2016-06-11 18:37:22
    文中较详细介绍GCD队列,各种GCD使用方法,实例如何使用Dispatch Source监听系统底层对象,分析不同锁的性能对比,实例GCD死锁情况。文中的Demo在这里 ... GCD(Grand Central Dispatch...GCD属于系统级的线程管理,在Dis
  • 文中较详细介绍GCD队列,各种GCD使用方法,实例如何使用Dispatch Source监听系统底层对象,分析不同锁的性能对比,实例GCD死锁情况。文中的Demo在这里 ... GCD(Grand Central Dispatch...GCD属于系统级的线程管理,在Dis
  • Unity打IOS版本遇到的问题

    千次阅读 2020-09-07 20:17:46
    在打包中涉及到证书,证书描述文件在第和第三部分中介绍。第二部分记录一些错误的解决方案,第四部分介绍app和ipa的区别。 目录 :一些值的查看 1.查看证书的描述文件的uuid字段值 2.查看证书的描述文件的...
  • iOS中的多线程基础

    2019-07-09 18:26:38
    NSThread是个苹果封装过的,面向对象的线程对象。但是它的生命周期需要我们自己来手动管理,所以使用不是很常见,比如[NSThread currentThread],它可以获取当前的线程类,你就可以知道当前线程的各种属性。 创建...
  • LLVM编译原理和使用

    万次阅读 多人点赞 2018-11-12 15:48:00
    LLVM简介: LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-...LLVM最早的时候是Illinois的个研究项目,主要负责人是Chris Lattner,他现在就职于A...
  • 以下部分内容摘自《iOS...因为Apple已经弃gdb投lldb,所以随着我动态调试的次数越来越频繁,gdb上个接个的bug经常会让人很恼火。既然苹果打算建立自己的调试器王国,也投入了钱力精力,那我们干脆也上手lldb玩玩,
1 2 3 4 5
收藏数 85
精华内容 34
关键字:

dis执行一次 swift