• Swift语法(更新)


    Swift语法(更新部分swift5)

    简介

    • 优于OC,快速,安全

    • 预编译指令包括宏定义(OC用的太多了)

    • 取消了OC指针和不安全访问的使用(看不到星星了)

    • 全部点语法,取消[ ]

    • 对Foundation框架做了很多改变,去除了NS,将绝大部分class转换成struct结构体(为了考虑性能和安全性,绝大部分使用结构体来代替以前的类,但是在实际使用感觉不到)

    • 以前是OC调UIKit,现在就是Swift调UIKit,调用的SDK没有变化

    • swift因为语法的严谨性可以让很多错误提前遇到,这样很少出现bug让程序停在main导致无法找到

    • @UIApplicationMain是程序的入口

    • 只有.h没有.m

    • 所有的代码都包括在{}里,默认方法func都有缩进

    • OC语法的allocInit替换成()

    • OS12.2的各种版本,包括macos,tvos等等,已经包含了swift5库,这意味着以前可能swift写的代码体积普遍大于OC,现在则整体做了体积压缩

    基础

    • 基本写法中不使self.,OC中的self.view之类写法可以省略self. ,self在闭包或者编译器提示的时候再使用

    • 分号是用来分割语句的,如果一行写很多,就可以加分号,一般时候可以不加

        let a = 10; let b = 15
        let aa = 10,bb = 15
    
    
    • #function打印执行的函数
    debugPrint(#function)
    
    • 添加标记用到// MARK: - 选择,如果是接下来要做的可以用// TODO:// FIXME:

    变量和常量

    • let:常量,如果从没被修改过,那就用let,不可变的更安全,所以尽量使用let,需要变化时候改var.像UI层的对象,采用let,但是还可以修改对应内的属性,例如let UIView,随后修改颜色,是因为本身地址没变

    • var:变量

    • 可以自动推导类型(Option + Click)整数默认Int,小数默认Double,但是不会做隐试转换,也就是说只要类型不同,无法直接计算

    • Swift对类型要求异常严格,任何不同类型的数据不能直接运算(哪怕是Int和Double),不会做一些自动的转换来转换成Double。Swift不存在基本数据类型,Int和Double都是结构体其实,强转用Double(x)完成,或者在定义的时候直接指定变量的类型let x : Double = 10;(很少使用)

    可选项(Optional)

    • 定义变量时,如果是可选的,表示可以有值,也可以是nil,用?

    • 强行解包 !,要少用,可能会崩

    • 最常见的错误:解包的时候发现nil。fatal error: unexpectedly found nil while unwrapping an Optional value

    • let可选的话,没有默认值,需要赋值。var可选的话,默认值为nil

    • 可选项在参与计算时候必须解包,所以为避免很多!,一般会采用if let/guard let的方式进行判断计算

    逻辑分支(if)

    • 三目运算符:Int(x!) > 5 ? print("dayu5") : print("xiaoyu5") 或者 Int(x!) > 5 ? print("dayu5") : () 这样就对后面的不作处理。()表示空执行。
    • swift中不存在非真既假,只有true和false

    不强行解包的方法

    1. 强行解包的写法:"??"。 一个简单的三目,这样无需做很多判断,是不是xy都不得0了。注意?? 必须加个(),因为??操作符号的优先级低,是最后判断的,所以不加()可能后面的都当做判断不执行了。??写法不需要解包了
        func demo(x: Int? , y : Int?) {
    
            print( (x ?? 0) + (y ?? 0) )
    
        }
    
    1. if let的写法:iflet/ifvar连用就可以进行判断,这个let的x1和y1作用于只在循环里,iflet/var也不需要解包了
            if let x1 = x , let y1 = y {
    
                print(x1 + y1)
    
            }
    
    1. guard let else的写法:和iflet相反,else里面一般是没值return,之后才执行需要的代码。guard可以降低分支层次,如果要执行的代码很多,本来在if里执行,{}就多了一层,如果用guard守卫一定有值才执行,这样层次就少了一层。guardLet很重要,也可以省略很多解包
            guard let x1 = x , let y1 = y else {
    
                print("x&y没值")
    
                return
            }
    
            print(x1 + y1)
    
    1. guardlet和iflet可以用同名变量接收。因为总会取名字,if let name = name这样就可以,注意后面使用的时候用非空的那个!并且iflet和guardlet可以依次判断,先判断是一个字典,再拿字典的数组,在判断数组的值,可以一条线判断出来。

    总结

        func demo() {
            let name: String? = "张三"
            let age: String? = "18"
            // 目的: 让可选项在不强行解包!的情况下做计算
            // 方式1:采用??
            print("方式1" + (name ?? "") + (age ?? ""))
            // 方式2:采用if let
            if let name = name, let age = age {
                print("方式2" + name + age)
            }
            // 方式3:采用guard let
            guard let nameA = name, let ageA = age else{
                return;
            }
            print("方式3" + nameA + ageA)
        }
    
    

    循环

    switch循环

    • OC中分支必须是整数,每一个语句都需要break,如果要定义局部变量需要{}

    • Swift可以任意判断,一般不需要break,多值判断用逗号,所有分支都至少需要一条指令,如果什么都不干,才要写break

        func demo(name : String) {
        
            switch name {
    
            case "guoguo","aixin":
    
                print("guoguo")
    
            default:
    
                print("shit")
    
            }
    
        }
    

    for循环

    • swift取消了i++++i
        for i in 0...5 {
    
        }
    
        // ...是闭区间,..<是开区间
        
        for i in 0..<5 {
    
    
        }
    
    • 反序遍历:
         for i in (0..<10).reversed() {
    
         }
    

    字符串

    • 用String,是一个结构体,具有绝大多数NSString功能,支持直接遍历

    • 遍历:

        func demo3() {
    
            let str = "wowosnshi是"
    
            for s in str {
    
                print(s)
    
            }
        }
    
    • 长度:
    
        // 返回指定编码对应的字节数,每个汉字三个字节
    
        print(str.lengthOfBytes(using: .utf8))
    
        // 返回真正字符串长度
    
        print(str.count)
    
    • 拼接:要注意可选项拼接不解决会带上Optional,剩下的都可以拼接,再也不用看StringWithFormat了

      let name = "AA"

      let age = 19

      let title : String? = "sss"

      print("(name)(age)(title ?? "")")

    • 格式化:

        let h = 8 , m = 10, s = 44
    
        // OC中用stringWithFormat格式化日期,Swift中可以
    
        let strDate = String(format: "%02d-%02d-%02d", h,m,s)
    
        print(strDate)
    
    • 4.2截取字符串
         func demo3() {
            let str = "hello world"
            // 截取前三个字符
            print(str.prefix(3))
            // 截取后三个
            print(str.suffix(3))
            // 截取3-6范围
            let start = str.index(str.startIndex, offsetBy: 3)
            let end = str.index(str.startIndex, offsetBy: 6)
            print(String(str[start..<end]))
        }
    

    数组:

    • 就是中括号,注意数组的类型,并且基本数据类型不需要包装,可以直接方数组里,如果类型不一样(混合数组,但是基本不用),自动推导[NSObject]。在Swift中还有一个[AnyObject类型],标示任意对象,因为在Swift中一个类可以没有任何父类。
       遍历:
    
            let array = ["张三","李四","王五"]
    
            // 遍历1(按照下标遍历)
    
            for i in 0..<array.count {
    
            }
    
            // 遍历2(遍历元素)
    
            for s in array {
    
            }
    
            // 遍历3(同时遍历下标和元素)
    
            for e in array.enumerated() {
    
                // let e: (offset: Int, element: String) e是一个元组
    
                print("(e.offset), (e.element)")
    
            }
    
            // 遍历4(同时遍历下标和元素)
    
            for (n,s) in array.enumerated() {
    
                print("(n),(s)")
    
            }
    
            // 反序遍历
    
            for s in array.reversed() {
    
            }
    
            // 反序索引下标(这样写才对,先枚举再反序)
    
            for (n,s) in array.enumerated().reversed() {
    
    
            }
    
    • 增删改:

      array.append("AA")
      
        array[1] = "BBB"
      
        array.remove(at: 2)
    • 合并:用“+”号。但是要合并的数组的两个类型必须一致。

    字典

    • 一般是[String:Any]

    • 增删改:和数组都类似,就是两个字典合并不像数组直接相加,而是需要遍历

    函数

    • Swift的类,结构体,枚举三种都有构造函数,都可以有方法,就像OC的类。枚举再swift变化很大,一般开发不会用到太高级语法。

    • 外部参数,当外部参数用_替代的时候,会在外部调用的时候忽略形参名

        func demo5(num1 a: Int,num2 b: Int) -> Int {
            return a + b
        }
    
    • 函数的默认值(OC不具备),这个使Swift比OC灵活很多很多,一个方法可以做很多事,因为OC会有各种参数和组合,Swift只需写一个最多的参数,然后不需要的设定默认值就是了
    • 无返回值 :直接省略 () Void都可以

    • 闭包:类似Block,比Block还广泛。OC中Block是匿名函数,Swift中函数是特殊的闭包。闭包在整个开发中和Block的应用场景一样。用于控制器/自定义视图/异步执行完成的回调。这些回调的特点就是都是以参数回调处理结果,返回值为Void。

        let biBao = { (x: Int) -> Int in
        return x + 100
        }
        print(biBao(10))
    
    • GCD:将任务添加到队列,指定执行任务的函数。任务就是Block/闭包,队列以同步/异步的方式执行。

      func loadData(compeletion:@escaping ( _ result: [String])->()) -> Void { DispatchQueue.global().async { print("耗时操作会获得一些结果 (Thread.current)") Thread.sleep(forTimeInterval: 1.0) let json = ["天气","不错","刮大风"] // 主线程回调 DispatchQueue.main.async(execute: { print("主线程更新UI (Thread.current)") // 回调 -> 通过参数传递 执行闭 compeletion(json) }) } } 调用:

      // 执行的适合我就拿到了值
      
        loadData { (result) in
            print("获取的新闻数据 (result)")
      
        }

    *尾随闭包:如果函数的最后一个参数是闭包,那么参数就省略了,最后一个参数直接{}大括号包装

    面向对象

    基本构造函数

    • ()就是allocInit,在Swift中对应init()。在swift中一个项目所有类都是共享的,可以直接访问,每一个类都默认有一个命名空间。A.name B.name God.name Dog.name。同一个类可以从属于不同的命名空间(假如有一个框架有Person类,做用户,还有一个框架做后台,也用Person。在OC中就只能靠前缀解决,HouTaiPerson,KuangJiaPerson。而Swift中的命名空间就是项目名。AAA项目有一个Person,那么AAA.Person就是AAA的Person类,此时再导入框架,那也是框架的.Person)

    • 只要是构造函数,就需要给属性设置初始值

    • 所谓构造函数,在oc中其实就是allocInit,在swift中目前只用init就行,一般的对象init时候会在前面默认加上override,意为重写,重写最大的特点是方法里面可以调用super.init()来实现父类的这个方法,而且super.init()是隐式的,意味着如果不写也会调用,但是程序员其实最害怕的是我什么都没写?为什么调用了?,所以为了完整的自我实现面向对象思想,还是要自己添加super.init()

    重载构造函数

    • 重载构造函数:(重写是父类有这个方法,override。重载是函数名相同,参数和个数不同。init就重写,init+参数就重载。OC是没有重载的!都是initWithXXXXX)。重载其实是最基本的方式,OC没有其实很low,但是Swift有。

    • 重载是所有方法都可以实现的,不局限于构造函数,例CGRect(,可以看到很多重载的实现

    • Nsobject类的isa属性就是为了记录当前状态的对象是什么的,就是一个student对象,云云。在swift中打p可以打印类内容

    KVC构造函数

    • KVC是OC特有的,是运行时特性,在运行时动态的发消息赋值,所以在KVC构造时候,super.init()应该先调用,随后再setValueForKey保证都创建完毕再调用。调用setValueForKey的时候,会判断赋值的对象是不是对象。要是对象的话就去判断是否已创建,没有创建则实例化。要是基本数据类型则不作处理,所以基本数据类型必须指定初始值来分配空间才行

    • 运行时可以获取到一个对象的所有属性方法等等,获取到了属性可以KVC进行字典转模型,这是所有第三方转模型插件的基础,获取到了方法,可以动态的发送各种方法。在swift中,如果一个基本变量Int什么的用?修饰了,可选了。此时运行时是找不到的,所以KVC会崩溃,同时,如果修饰了private属性,运行时也是找不到,KVC也会崩溃。其实在OC中,private是基本不用的,而且存不住任何私有,都可以被运行时找出来。但是swift就是真的藏起来了,找不到这个属性和方法了外界

    • 一般在模型中加个?,然后用KVC实现(先调用init因为是运行时机制)

    • 模型中属性定义:基本数据类型 = 0,对象设置?

    运行时中,基本类型设置? 属性设置私有都会让运行时拿不到,此时kvc就会出错。

    • 标准写法
        @objc var name: String?
        @objc var age: Int = 0
        
        init(dict: [String : Any]) {
            super.init()
            setValuesForKeys(dict)
        }
        
        override func setValue(_ value: Any?, forUndefinedKey key: String) {
        }
    
    
    • 如果子类没有重写父类方法,调用的时候就会直接调用父类的方法。当继承一个类,就继承所有属性和方法,包括KVC。当PERSON写好了KVC后,STudent即便什么也不写,也有父类的方法

    便利构造函数

    • 关键字Convenience

    • 目的:条件判断,只有满足条件才实例化对象,防止不必要的内存开销,简化对象创建。本身是不负责属性的创建和初始化的。

    • 说白了就是有些函数 带个? 返回值可以返回空,这就是便利构造函数的功劳。由于函数里多为判断,是否要构造,不符合构造条件就不创建,而创建的过程需要调用别的构造函数才能实现,所以最后都要调用self.init(xxxxxxx)

    • 主要用于条件检查和控件创建。

    • 在控件创建的时候,就不用?了,因为我就是为了创建控件的。

    • 不能被继承,不能被重写

    • deinit:类似OC的Dealloc

    • 便利构造函数 + 分类可以省略抽取很多代码。例如给UITextField/UIButton写分类,然后写便利构造函数,方便。

  • 相关阅读:
    谷歌机器学习
    Pycharm使用conda安装的环境
    HAN模型理解2
    HAN模型理解1
    RCNN
    深度CNN
    多通道CNN
    TextCNN
    词向量2
    词向量1.md
  • 原文地址:https://www.cnblogs.com/sgxx/p/6093807.html
Copyright © 2020-2023  润新知