美文网首页iOS Developer
Swift ? 和 ! 总结

Swift ? 和 ! 总结

作者: MonkeyHeng | 来源:发表于2017-05-26 23:22 被阅读87次

    很多人在学习Swift的时候会搞不清楚?!的区别,本人今天对于这两个标识符做一个细细的调研

    有很多人对于?!有一个误区,那就是?代表是可以nil的变量,!是不是nil的变量,实际上这个判断是错误的,他们两个区别是可选类型的拆包方式不一样

    什么是可选类型?

    在Swift中,可选类型其根源是一个枚举型,里面有None和Some两种类型。其实所谓的nil就是Optional.None, 非nil就是Optional.Some,如下例所示

    var a:String
    var b:String?
    print(type(of: a)) // String
    print(type(of: b)) // Optional<String>
    

    根据例子可以看出来a就是String类型,而b是Optional<String>(可选的String类型)

    你也可以这样声明一个可选类型

    var a:String = Optional.some("a")
    var a:String? = "a"
    

    这样两种方式是一样的,官方文档中写到

    You use the Optional type whenever you use optional values, even if you
    never type the word Optional. Swift's type system usually shows the
    wrapped type's name with a trailing question mark (?) instead of showing
    the full type name. For example, if a variable has the type Int?, that's
    just another way of writing Optional<Int>. The shortened form is
    preferred for ease of reading and writing code.

    你可以不是用Optional关键字来声明一个可选类型,而是用?系统会自动的帮你把该类型声明为可选类型

    下面看一下Optional到底什么,查看源码得知:

    The Optional type is an enumeration with two cases. Optional.none is
    equivalent to the nil literal. Optional.some(Wrapped) stores a wrapped
    value. For example:

    let number: Int? = Optional.some(42)
    let noNumber: Int? = Optional.none
    print(noNumber == nil)
    // Prints "true"
    

    可选类型是一组枚举值,Optional.none等于nilOptional.some(Wrapped) 存着一个被包装的值.

    下面是可选类型的定义

    public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    
        case none
        
        case some(Wrapped)
    
        public init(_ some: Wrapped)
    
        public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
    
        public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
    
        public init(nilLiteral: ())
    
        public var unsafelyUnwrapped: Wrapped { get }
    }
    

    Swift是类型安全的语言,一个String类型的变量不可能既能是具体的字符串,又可以为nil(更严格的说String类型的内容只能是字符串)。所以,在Swift中有了可选类型的概念。(其实这一概念也是“借鉴”于其他编程语言,比如C#,只不过在C#中称之为可空类型)

    可选类型的出现可以解决,当值不存在的情况下如何处理。可选类型允许变量为空,而非可选类型不可以

    var a:String
    print(a) // 无法执行 变量a没有值需要初始化
    
    var b:String?
    print(b) // 可以执行打印 nil
    

    可选类型之所以可选,就是因为它可以是有值的也可以是没有值的,而非可选类型则不可以,非可选类型必须是有值的

    那?和!到底有啥区别?

    上文说了什么是可选类型,只要加?或者!那么它就是可选类型

    虽然两种符号的修饰都是可选类型,它们还是有区别的,它们的区别在于拆包方式不一样

    let a:String? = "a" //可选类型
    print(type(of: a))                          // Optional<String>
    print(a)                                    // Optional("a")
    let b:String! = "b" //可选类型              
    print(type(of: b))                          // ImplicitlyUnwrappedOptional<String>
    print(b)print                               // b
    

    通过打印我们知道?是一个可选类型,并且打印出来的值是包装过的值,而!是一个ImplicitlyUnwrappedOptional可选类型,他会立刻拆包又叫隐式拆包

    隐式拆包

    var num: Int! = 20  
    var numValue: Int = num // 自动解包  
    

    那如何将?声明的可选类型拆包呢,这里就要用到显式拆包了:

    显式拆包

    var num: Int?  
    var numValue: Int = num!  // 主动拆包 这里会报错因为讲一个nil赋值给非可选类型了
    

    别急还有as、as?和as!有啥区别

    as?as!都是向下转型

    向上转型:subClass->supClass

    向下转型:supClass->subClass

    当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值,并且若下转是不可能的,可选值将是 nil。这使你能够检查向下转型是否成功。

    let array:NSArray = NSArray.init(object: "111")
    let mutableArray:NSMutableArray! = array as? NSMutableArray
    print(mutableArray) // nil
    
    let array:NSArray = NSMutableArray.init(object: "222")
    let mutableArray2:NSMutableArray! = array as? NSMutableArray
    print(mutableArray2) // 222
    

    只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。

    // 运行时错误
    let array:NSArray = NSArray.init(object: "111")
    let mutableArray:NSMutableArray = array as! NSMutableArray
    
    // 正常运行
    let array:NSArray = NSMutableArray.init(object: "111")
    let mutableArray:NSMutableArray = array as! NSMutableArray
    

    as是向上转型或者类型判断

    仅当一个值的类型在运行时(runtime)和as模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成as模式左边指定的模式

    向上转型

    let array1 = NSMutableArray()
    let array2 = array1 as NSArray
    

    类型判断

    类型判断仅限于在Switch中使用

    let array1 = NSMutableArray()
    switch array1 {
    case array1 as NSString:
        break
    case array1 as NSMutableArray:
        break
    default:3
        break
    }
    if array1 as NSMutableArray {
        // 报错
    }
    

    最后:几个小tips

    tip1:非可选类型不可以赋值nil

    // 报错不可以
    let value:NSString = nil
    

    tip2:as!转换的是非可选类型 as?转换的是可选类型

    let array:NSArray = NSMutableArray.init(object: "111")
    print(array as! NSMutableArray)
    /*
     (111)
    */
    print(array as? NSMutableArray)
    /*
    Optional(<__NSArrayM 0x7b790430>(111))
    */
    

    tip3:!非可选类型赋值的时候可以在值后面加!获取非可选类型变量 但是要确定值一定有值 否则运行时报错

    let array1:NSArray? = NSMutableArray.init(object: "111")
    let array2 = array1! // 运行正常
    
    var array3:NSArray! = NSArray()
    array3 = nil
    let array4 = array3!  // 运行时报错 不可将nil赋值给可选类型
    

    tip4:一个可选类型变量加上!就变成非可选类型了

    let aaa:String? = "1"
    print(type(of: (aaa)!)) // String
    

    tip5:非可选类型可以通过加!转换成可选类型 但是可选类型却不能通过加?转换成非可选类型

    let aaa:String? = "1"
    print(type(of: (aaa)!)) // String
    
    let bbb:String = "2"
    print(type(of: (bbb)?)) // 检查报错
    

    在学习Swift的过程中,?!深深的困扰着我,这篇文章也算是我学习的一个总结,对这两个符号理解清楚非常有助于以后的学习,这两个符号的出现最大的好处就是规范开发者在声明变量的时候做好类型处理,可以有效的减少代码中因为类型判断而出现的无用代码,如果那里有不正确的地方,还望指出。

    最后几个小tips是我在写程序的总结的,不一定全对,暂时总结的就这些

    相关文章

      网友评论

        本文标题:Swift ? 和 ! 总结

        本文链接:https://www.haomeiwen.com/subject/pktunttx.html