您的位置:首页 > 其它

存储安全:确保变量先定义再使用

2015-06-12 09:10 381 查看

本文来自南栀倾寒(简书)的投稿,翻译自苹果Swift博客,原文:Memory Safety: Ensuring Values are Defined Before Use
欢迎通过“投稿爆料”渠道或者support@cocoachina.com投稿
我们在用swift设计开发时的一个重点就是如何提高编码模型的内存安全问题。内存安全涉及到很多个方面,所以这篇文章会从一些基础的内容开始,并包含一个简单用例:如何确保变量有一个初始值才能使用。
swift版方法变量什么时候是安全的?开发者认为应该是无论任何时候访问一个变量,它都有一个可用的有效值。语言采用不同的方法以确保安全,像其他编程语言,比如C,要求程序员对内存模型的编程技术非常严格,但是这是有风险的,人总会犯错。C++和OC执行了强制性的模式来改善这种可能造成错误的地方 ,而其他语言则采取了一些极端的措施。Swift使用的主要技术是使用高级编译器执行代码数据流分析。编译器会强制变量在被使用之前进行初始化,这一策略被称为Definitive Initialization。比如Java和C#(尤其)都采用了这种技术。Swift对更广泛范围内的变量使用了这种方法的扩展版本。注意:文章末尾包含其他技术的信息,Swift在某种程度上使用了其中的大部分。初始化局部变量Swift在不少上下文环境中使用了Definitive Initialization的规则,不过局部变量使用的是最简单的。Definitive Initialization比隐式默认初始化规则更为灵活(参看下文),因为类型推断允许你这样写:
var myInstance : MyClass  // Uninitialized non-nullable class reference
if x > 42 {
myInstance = MyClass(intValue: 13)
} else {
myInstance = MyClass(floatValue: 92.3)
}
// Okay because myInstance is initialized on all paths
myInstance.printIt()
编译器可提供if语句两端的验证来初始化myInstance,可以保证不会调用那些未初始化的内存。Definitive initialization是一个很强大的方法,但仅在它是可靠和可预测的时候非常有用。当你有更复杂的控制流时它会让你大吃一惊,比如下边:
var myInstance : MyClass
if x > 10 {
myInstance = MyClass(intValue: 13)
}
// ...
if x > 42 {
myInstance.printIt()
}
这时编译器可能告诉你 “Variable myInstance used before initialized”在调用printIt()。其实就是说变量未初始化,因为编译器不可能做所有预测或者类型推断,这就要求我们不要写这么复杂的逻辑去初始化一个变量。我们可以让编译器去处理个别的用例,不可能处理所有的用例(这么做相当于halting problem),所以我们要保持编译器规则的简单和可预测性。Swift让初始化一个变量变得非常简单。实际上,在普通数据类型如int初始化时可以直接写var x = 0,给变量一个0初始值,而不是声明未初始化变量var x : Int。可能的情况下,Swift支持初始化的外显性。当使用init()调用时,就有很多强大的方法来初始化一个变量。你可以在Swift Programming Language的"Initialization" 一节中阅读更复杂一些的内容。其他技术补充除了definitive initialization,Swift还在语言的重点范围使用了附加方法。你可以在其他语言中使用这些技术,所以我们在这篇文章会说的很简短。每种方法都有不足,所以它们不是Swift使用的主要方法:将安全问题留给程序员:鉴于C语言的流行程度,所以理解simply leaving safety up to the developer的优缺点非常重要。不幸的是,使用未初始化的值在C语言中产生的未定义的行为(undefined behavior),通常会导致运行时问题(explosions)。C语言依赖程序员不犯错误。由于我们的目标是让Swift默认是安全的,所以一般不会使用这个方法。不过,当明确需要的时候,类似UnsafePointer的API允许你明确地选择不安全性。

隐式初始化:有些值可通过编译器的隐式初始化来保证其安全性,比如设置一个“zero value”,类似Objective-C对实例变量做的那样,或者通过运行默认构造器

(initializer)来实现,比如C++中的。我们对此进行了深度探索,但是最终决定不对其进行广泛的使用,原因是:如果有些Protocol没有要求实现init()方法 这样一个变量就可能引用一个未初始化的对象遗留在Cocoa编程中,这种情况在Swift中很常见。

即便是基本类型,比如整数0经常是错误值。这是在Swift中设置初始值如此简单的一个原因。对于维护人员来说,就算不给变量默认值,自己写一个也并不麻烦,而且会提前发现很多错误,使代码更容易维护。

注意:对于可空类型值来说,默认初始化nil是正确的,所以所有Optional和ImplicitlyUnwrappedOptional类型会默认初始化为nil.定义时要求构造器:在定义变量时,程序员通常要提供一个初始值,也就是说var x : Int没有构造器是不合法的。虽然在函数式语言中这是一个常见的方法,不过这是个非常沉重的要求,因为它强制执行了一个非常严格的编程风格,有碍自然模式的表达。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: