2016-07-19 14:39:09 jyqj123 阅读数 1654

在Swift中变量是可以设置为可选的如下 (以下没有特殊声明,均在xCode8.0beta, Swift3.0中测试的)

var aString:String?  // var aString:Optional<String>;

那如何在Swift解包呢。 这里有几种解包方式:

第一种 强制解包 使用符号?拆包

例如:

var aString:String? = "Testttttttttt"
print(aString!) //  Testttttttttt   - 强制解包
print(aString)  // Optional("Testttttttttt")

但是这有个问题。假如aString没有被赋值,强制解包时,程序会直接闪退。所以这种是不安全的解包方法。(不安全解包)

第二种 隐式拆包变量声明

例如:

var aString:String! = "Testttttttttt"
print(aString!)
print(aString)

没有初始化时,就打印aString,程序会直接闪退。所以这种是不安全的解包方法。(不安全解包)
但是在以下情况,就必须使用隐式解包
* 1. 对象属性在初始化的时候不能nil,否则不能被初始化。典型的例子是Interface Builder outlet类型的属性,它总是在它的拥有者初始化之后再初始化。在这种特定的情况下,假设它在Interface Builder中被正确的配置——outlet被使用之前,保证它不为nil。
* 2. 解决强引用的循环问题。当两个实例对象相互引用,并且对引用的实例对象的值要求必须有值时候。在这种情况下,引用的一方可以标记为unowned,另一方使用隐式拆包。

第三种 可选绑定

例如:

var aString:String?
if let str = aString {
    print(aString!)
} else {
    print("aString have no value")
}

不管aString有没有初始化,在if中都可以放心去拆包。aString没有初始化或者没有值(nil),程序都不会进入if代码块中,所以这是一种(安全解包)

第四种 可选链 (自判断链接)

可选链是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil,如果目标有值,调用就会成功,如果目标为nil,调用将返回nil,多次请求或调用可以被链接成一个链,如果任意一个节点为nil将导致整条链失效。通俗的来说就是请求和调用属性、方法时,含有一个或多个的返回值为可选值的一次调用就是可选链

注意:Swift 的自判断链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到
例如:

class Clazz {
    var clazzName:String?
}

class Student {
    var name:String?
    var clazz:Clazz?

    init() { }
    init(_ clazz:Clazz) {
        self.clazz = clazz
    }

    func getName() -> String? {
        return name;
    }
}=
let c:Clazz = Clazz()
let p:Student = Student()
let p1:Student = Student(c)
print(p.clazz?.clazzName)  // 打印可选链   nil
print(p1.clazz?.clazzName)  // 打印可选链  nil

c.clazzName = "终极一班"
print(p1.clazz?.clazzName)  // 打印可选链  // Optional("终极一班")

运行之后,p的属性clazz没有被初始化,程序也不会闪退。p1的属性clazz被初始化了,但是打印没有被初始化的clazzName属性时,程序照常运行。可见可选链也是(安全解包)
最后调用的是一个可选值,那么返回的也是一个可选值

第五种 Nil Coalescing (空值合并运算符) , swift3.0以后 不支持

例如: (xCode7.3 Swift2.0中测试)

var aString:String?
print (aString ?? "Tesssst")

第六种 guard 语句

func testStr(aString:String?) {
    guard let str = aString where str != "Test" else {
        print("Not Satisfied!")
        return
    }
    // str is available to use
    print("Satisfied!")
}
testStr(aString: "Test---")  // Satified!
testStr(aString: "Test")    // Not Satified!

guardif 的可选绑定有点类似,但是guard更好。guard主要有一下作用:
* 1. 对你所期望的条件做检查。如果条件不符合,guard的else语句就运行,从而退出这个函数。所以需要在else代码块中退出操作
* 2. 如果通过了条件判断,可选类型的变量在guard语句被调用的范围内会被自动的拆包。 (安全解包)
* 3. 对你所不期望的情况早做检查,使得你写的函数更易读,更易维护。

2015-07-28 17:12:11 ERIC_Dream 阅读数 397

swift可选类型解包方式有三种:
第一种:

var string:NSString?
 if string != nil{
    println(string!)
    }else{
       println("nil")
    }

如果不为空就强制解包
第二种:

var string:NSString?
 println(string ?? "nil")

第三种:

var string:NSString?
if var unwrap = string{
    println(string!)
    }else{
       println("nil")
    }
2017-05-24 09:41:55 g6uqwseseo 阅读数 177

不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包。举个例子,我在 OC 中定义这样一个变量:

@property (nonatomic, copy) NSString *foo;

它转成 Swift 就变成了这样:

var foo: String!

这样看上去合情合理。Swift 中有 String? 和 String! 两种形式,但 OC 中没有 NSString? 和 NSString! ,当 Swift 无法区分 OC 中的变量是不是 nil 的时候,一律强行转化为非空参数。这样设计体现了 Swift 强安全性语言的特性。

但是这时候问题来了。我们回到上文中的例子,假如 OC 中对 foo的使用如下:

@property (nonatomic, copy) NSString *foo;

- (void)secretFunc {  
  // 一些诡异复杂的操作
  ...  
  self.foo = nil;
}

然后我们在 Swift 中这样调用:

func init() {
  objectiveCObject.secretFunc()

}func calcLen() -> Int {  

  return objectiveCObject.foo.characters.count

}

上面这段 Swift 代码执行到calcLen()时会崩溃,原因是foo在init()中已经被设成了 nil,而foo在 Swift 中是 foo!。也就是说,因为 Swift 对 OC 变量的强转,导致了程序的崩溃。这是一个很容易忽略的问题,因为强转的时候,Xcode 不会给出任何的警告、报错或是提醒。而我们作为开发者,很容易忽略这样的错误,导致 runtime 的时候直接崩溃。

针对这种情况,我们来讨论下面三个问题。

  • Q: 为什么 Swift 要将 OC 中的变量如foo转为foo!而不是foo?

这是一个有争议的话题。我个人认为强制解包的方式会督促开发者考虑变量是否为 nil 的问题。在 OC 时代,声明变量一般不会考虑是否为空的问题;而在 Swift 时代,因为其是一门强安全性的语言,在变量定义时,必须确定变量是否为空。一般定义为非空有两种以下形式:

// force unwrapping
var foo = "Hello"
// implicitly unwrapping
var foo: String!

前者根据初始值强制解包,定义 foo 为非空变量;后者则直接申明 foo 为非空变量。

无论哪种情况,开发者会从一开始就思考处理 nil 时的情况,并在后续开发中一直注意。所以从foo转化为foo!,你就会思考 OC 中代码是否也要处理
nil 的情况;而如果转化为foo?,nil 也无所谓,而实际可能并不是这样,nil 的特殊情况考虑会一直忽略,开发中的隐患一直存在,同时也不符合 Swift 强安全性的设计思路。

  • Q: 我就想让 OC 中的变量从foo转化到 Swift 中变成foo?,有没有办法

请这样在 OC 中定义变量:

// foo -> foo?
@property (nullable, nonatomic, copy) NSString *foo;
// bar -> bar!
@property (nonnull, nonatomic, copy) NSString *bar;
// qux -> qux!
@property (nonatomic, copy) NSString *qux;

这种事先声明是否为 null 的定义方法,是不是很像 Swift 中的 optional 机制?然而 OC 时代我们几乎不会去管变量是否为 nullable 这件事,由此我们可以体会 OC 和 Swift 在语言设计思路上的差异。

其实nullable和nonnull是 Swift 出来之后才引入 OC 的。所以一开始,OC 中的变量默认都是nullable,转变到 Swift 中,应该就是?。但是这样转化代价太大,我们所有变量都要在 Swift 中用if else或者guard来解包。所以为了写起来方便,Swift 干脆直接强转,故而现在这个机制也是一个历史遗留问题。

  • Q: Swift 如此这般导致混编 App 崩溃,没有提示的情况下程序员必须细细检查 nil 导致的 bug,这样设计强制解包的代价是否有点大?

这个 bug 在混编 App 中很容易出现,没有警告确实带来很大困扰。实际上这个问题早就在苹果的开发者论坛上被提出,Swift 组自己也开了个 ticket 要修,可惜最后不了了之。Github 上有人开发出了第三方的工具来解决这个问题。

我个人觉得这个问题苹果不重视的原因在于 Swift 和 OC 混编只是一个暂时的局面。Swift 取代 OC 是一个时间问题,所以解决混编中的问题都显得没有多大意义,在苹果内部也一直是低优先级。毕竟现在所有精力应该放在 Swift 上,随着时间的推移和 OC 的淡出,这个问题也将微不足道。

2017-02-09 10:31:11 qq_18674153 阅读数 1578

在Swift中有可选型这一类型,但不能确定变量是否为空时就使用的话,编译器会直接报错



这时候就要解包。在变量后面加上一个!,意思是说对编译器说这个变量不为nil 



这里会有一个问题,假如这时候errorCode 为空会出现什么样的情况呢。编译器直接报错,因为这是在playground里写的代码,所有错误会直接显示出来,但在真正的项目中就不一定了。



所以这时要对errorCode进行判断



也可以对errorCode做解包,下图是errorCode为nil



errorCode不为nil



同时对两个变量进行解包


2017-07-04 15:54:45 hbblzjy 阅读数 991

不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包。举个例子,我在 OC 中定义这样一个变量:

@property (nonatomic, copy) NSString *foo;

它转成 Swift 就变成了这样:

var foo: String!

这样看上去合情合理。Swift 中有 String? 和 String! 两种形式,但 OC 中没有 NSString? 和 NSString! ,当 Swift 无法区分 OC 中的变量是不是 nil 的时候,一律强行转化为非空参数。这样设计体现了 Swift 强安全性语言的特性。

但是这时候问题来了。我们回到上文中的例子,假如 OC 中对 foo的使用如下:

@property (nonatomic, copy) NSString *foo;

- (void)secretFunc {
  // 一些诡异复杂的操作
  ...

  self.foo = nil;
}

然后我们在 Swift 中这样调用:

func init() {
  objectiveCObject.secretFunc()
}

func calcLen() -> Int {
  return objectiveCObject.foo.characters.count
}

上面这段 Swift 代码执行到calcLen()时会崩溃,原因是fooinit()中已经被设成了 nil,而foo在 Swift 中是 foo!。也就是说,因为 Swift 对 OC 变量的强转,导致了程序的崩溃。这是一个很容易忽略的问题,因为强转的时候,Xcode 不会给出任何的警告、报错或是提醒。而我们作为开发者,很容易忽略这样的错误,导致 runtime 的时候直接崩溃。

针对这种情况,我们来讨论下面三个问题。

  • Q: 为什么 Swift 要将 OC 中的变量如foo转为foo!而不是foo?

这是一个有争议的话题。我个人认为强制解包的方式会督促开发者考虑变量是否为 nil 的问题。在 OC 时代,声明变量一般不会考虑是否为空的问题;而在 Swift 时代,因为其是一门强安全性的语言,在变量定义时,必须确定变量是否为空。一般定义为非空有两种以下形式:

// force unwrapping
var foo = "Hello"

// implicitly unwrapping
var foo: String!

前者根据初始值强制解包,定义 foo 为非空变量;后者则直接申明 foo 为非空变量。

无论哪种情况,开发者会从一开始就思考处理 nil 时的情况,并在后续开发中一直注意。所以从foo转化为foo!,你就会思考 OC 中代码是否也要处理
nil 的情况;而如果转化为foo?,nil 也无所谓,而实际可能并不是这样,nil 的特殊情况考虑会一直忽略,开发中的隐患一直存在,同时也不符合 Swift 强安全性的设计思路。

  • Q: 我就想让 OC 中的变量从foo转化到 Swift 中变成foo?,有没有办法

请这样在 OC 中定义变量:

// foo -> foo?
@property (nullable, nonatomic, copy) NSString *foo;

// bar -> bar!
@property (nonnull, nonatomic, copy) NSString *bar;

// qux -> qux!
@property (nonatomic, copy) NSString *qux;

这种事先声明是否为 null 的定义方法,是不是很像 Swift 中的 optional 机制?然而 OC 时代我们几乎不会去管变量是否为 nullable 这件事,由此我们可以体会 OC 和 Swift 在语言设计思路上的差异。

其实nullablenonnull是 Swift 出来之后才引入 OC 的。所以一开始,OC 中的变量默认都是nullable,转变到 Swift 中,应该就是?。但是这样转化代价太大,我们所有变量都要在 Swift 中用if else或者guard来解包。所以为了写起来方便,Swift 干脆直接强转,故而现在这个机制也是一个历史遗留问题。

  • Q: Swift 如此这般导致混编 App 崩溃,没有提示的情况下程序员必须细细检查 nil 导致的 bug,这样设计强制解包的代价是否有点大?

这个 bug 在混编 App 中很容易出现,没有警告确实带来很大困扰。实际上这个问题早就在苹果的开发者论坛上被提出,Swift 组自己也开了个 ticket 要修,可惜最后不了了之。Github 上有人开发出了第三方的工具来解决这个问题。

我个人觉得这个问题苹果不重视的原因在于 Swift 和 OC 混编只是一个暂时的局面。Swift 取代 OC 是一个时间问题,所以解决混编中的问题都显得没有多大意义,在苹果内部也一直是低优先级。毕竟现在所有精力应该放在 Swift 上,随着时间的推移和 OC 的淡出,这个问题也将微不足道。

参考链接:
Nullability and Objective-C

swift中的nil

阅读数 1464

没有更多推荐了,返回首页