- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我真的很困惑地发现下面的代码只是拒绝与典型的“Unexpectedly found nil while unwrapping an Optional value”异常一起崩溃,这是你期望从强制展开 bar
。
struct Foo {
var bar: Bar?
}
struct Bar {}
var foo = Foo()
debugPrint(foo.bar) // nil
debugPrint(foo.bar!.dynamicType) // _dynamicType.Bar
dynamicType
似乎以某种方式 能够回退到 bar
的定义类型——而不会崩溃。
请注意,这似乎仅在 Bar
被定义为 value 类型(如 @dfri says )时发生,Foo
是 struct
或 final class
(如 pointed out by @MartinR )& foo
是可变的。
我最初确实认为它可能只是编译器优化,因为 bar
的类型在编译时是已知的,因此强制展开可以被优化掉——但它当 Bar
被定义为 final class
时也会崩溃。此外,如果我将“优化级别”设置为 -Onone
,它仍然有效。
我倾向于认为这是一个奇怪的错误,但需要一些确认。
这是 dynamicType
的错误或功能,还是我只是在这里遗漏了什么?
(使用带有 Swift 2.2 的 Xcode 7.3)
这在 Swift 4.0.3 中仍然可以重现(用一个更小的例子):
var foo: String?
print(type(of: foo!)) // String
这里我们使用 dynamicType
的继承者 type(of:)
来获取动态类型;和前面的例子一样,它不会崩溃。
最佳答案
这是 indeed a bug ,已修复by this pull request ,它应该会进入 Swift 4.2 版本,一切顺利。
如果有人对复制它的看似奇怪的要求感兴趣,这里有一个(不是真的)简要概述发生了什么......
调用标准库函数 type(of:)
被类型检查器解析为特殊情况;它们在 AST 中被一个特殊的“动态类型表达式”所取代。我没有调查它的前身 dynamicType
是如何处理的,但我怀疑它做了类似的事情。
当为这样的表达式发出中间表示(具体为 SIL)时,the compiler checks to see如果生成的元类型是“厚”的(对于类和协议(protocol)类型的实例),如果是,则发出子表达式(即传递的参数)并获取其动态类型。
但是,如果生成的元类型是“瘦”的(对于结构和枚举),编译器会在编译时知道元类型值。因此,仅当子表达式有副作用时才需要对其求值。这样的表达式作为“忽略的表达式”发出。
问题出在发出被忽略的表达式的逻辑上,这些表达式也是左值(一个可以分配给并作为 inout
传递的表达式)。
Swift 左值可以由多个组件组成(例如,访问属性、执行强制解包等)。一些组件是“物理的”,这意味着它们会产生一个要使用的地址,而其他组件是“逻辑的”,这意味着它们由 getter 和 setter 组成(就像计算变量一样)。
问题是物理组件被错误地认为是无副作用的;然而,强制展开是一个物理组件,不是没有副作用(键路径表达式也是一个非纯物理组件)。
因此,如果表达式左值仅由物理组件组成,则带有强制展开组件的忽略表达式左值将错误地不评估强制展开。
让我们看一下当前崩溃的几个案例(在 Swift 4.0.3 中),并解释为什么该错误被回避并且正确评估了强制解包:
let foo: String? = nil
print(type(of: foo!)) // crash!
这里,foo
不是左值(因为它被声明为 let
),所以我们只是获取它的值并强制解包。
class C {} // also crashes if 'C' is 'final', the metatype is still "thick"
var foo: C? = nil
let x = type(of: foo!) // crash!
这里,foo
是一个左值,但编译器认为生成的元类型是“厚”的,因此取决于 foo 的值!
,因此加载了左值,因此计算了强制解包。
我们也来看看这个有趣的案例:
class Foo {
var bar: Bar?
}
struct Bar {}
var foo = Foo()
print(type(of: foo.bar!)) // crash!
它会崩溃,但如果 Foo
被标记为 final
则不会。无论哪种方式,生成的元类型都是“瘦”的,那么 Foo
是 final
有什么区别呢?
嗯,当 Foo
是非 final 时,编译器不能仅通过地址引用属性 bar
,因为它可能被子类覆盖,这很可能将其重新实现为计算属性。因此,左值将包含一个逻辑组件(对bar
的getter 的调用),因此编译器将执行加载以确保此getter 调用的潜在副作用被评估(力展开也将在负载中评估)。
然而,当 Foo
是 final
时,对 bar
的属性访问可以建模为物理组件,即它可以被引用地址。因此编译器错误地假设因为所有左值的组件都是物理的,所以它可以跳过对它的评估。
无论如何,这个问题现在已经解决了。希望有人觉得上面的漫谈有用和/或有趣:)
关于swift - 为什么在强制解包的 nil 可选值类型上使用 dynamicType 有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36824048/
假设我有一个协议(protocol): protocol VehicleModel {...} 它是由许多不同的结构实现的。 (例如 CarModel、TruckModel 等)我有一个通用方法来获取
考虑下面的例子: class P {} class C: P {} let i: P = C() let iDT = i.dynamicType let CS = C.self iDT == CS i
我有这些类(class): AppDataProtocol.swift public protocol AppDataProtocol{ var logoImagePath : String!
基本上我想为 UIViewController 或任何其他默认的 swift 类添加一个类型别名。这背后的原因是我想抽象我的代码,这样我就可以通过使用 this 而不是 self.dynamicTyp
我真的很困惑地发现下面的代码只是拒绝与典型的“Unexpectedly found nil while unwrapping an Optional value”异常一起崩溃,这是你期望从强制展开 b
我正在尝试生成一个实现接口(interface)的动态类,但是其中一个或多个成员已经存在于基础中。我在 C# 中编译了以下代码,并在反射器中检查它以查看 C# 编译器的作用。 class BaseCl
有没有办法在 Swift 2.1 中实现这一点 class Apple { } class Fruit { let apples = [Apple](); } let fruit = Frui
我刚刚更新到 Xcode 8 和 iOS 10(使用旧版 Swift 语言版本)。 尝试再次编译我的项目一直很痛苦,即使仍然使用旧的 Swift 语法。这次我的一个函数使用了 NSBundle(for
你好,这是我的第一篇文章,如果我做错了什么,请不要太难过:D 我正在为一个大程序写一个 DeSerializer,为此,我有自己的类(class) public class DeSerializeab
在某些情况下:tableView 有不止一种类型的 UItableViewCell,并且这些单元格实现了函数“setData(_:)”。所以,在 "tableView(_ tableView: UIT
在我的应用程序中,我正在序列化消息以使用 protobuf-net 通过网络发送。每条消息都有一个标题信息的键值对列表。 但是,我遇到了一个异常,我已经能够用一个非常简单的例子重现它: [TestFi
我是一名优秀的程序员,十分优秀!