-
Oct 7, 2014
目前Swift 博客关注在先进的编程话题,包括Swift 语言的设计概念。我们认为这对新开发者和该接触Swift 的开发者是有帮助的。为了让每个人都能够使用,我们放了一段小的视频,演示如何从零开始用Swift创建一个应用,用少于10分钟的时间。
So far the Swift blog has focused on advanced programming topics, including the design principles of the Swift language. We thought it would be helpful to provide content for programmers who are new to Swift and just trying Xcode for the first time. To make it more approachable for everyone, we put together a very short video that demonstrates how to build an iOS app in Swift from scratch, in less than ten minutes.
观看视频
-
Sep 25, 2014
两个偶尔有用的C的特性是__FILE__和__LINE__这两个神奇的宏。这些是被创建的预处理器,在C解析器运行前扩展出来。尽管没有一个与处理器,Swift 提供了非常类似的功能,名字也很相似,但是运行原理却很不同
Two occasionally useful features of C are the __FILE__ and __LINE__ magic macros. These are built into the preprocessor, and expanded out before the C parser is run. Despite not having a preprocessor, Swift provides very similar functionality with similar names, but Swift works quite differently under the covers.
-
内置标示符Built-In Identifiers
正如指导中描述的,Swift 有一系列内置标示符如__FILE__,__LINE__,__COLUMN__和__FUNCTION__。这些表达式可以用于任何地方,有解析器扩展到了字符串或者整型文字,在源代码中对应到当前位置。这是对手动记录非常有用的,例如退出前打印当前位置。
然而,如果如下定义assert,并不会帮助我们实现assert():
As described in the Swift programming guide, Swift has a number of built-in identifiers, including __FILE__, __LINE__, __COLUMN__, and __FUNCTION__. These expressions can be used anywhere and are expanded by the parser to string or integer literals that correspond to the current location in the source code. This is incredibly useful for manual logging, e.g. to print out the current position before quitting.
However, this doesn’t help us in our quest to implement assert(). If we defined assert like this:
func assert(predicate : @autoclosure () -> Bool) {
#if DEBUG
if !predicate() {
println("assertion failed at \(__FILE__):\(__LINE__)")
abort()
}
#endif
}
以上代码会实现assert()本身,打印文件/线的位置,而不是从呼叫者的位置。没有作用。
The above code would print out of the file/line location that implements assert() itself, not the location from the caller. That isn’t helpful.
-
得到呼叫者的位置Getting the location of a caller
Swift借鉴了D语言的一个聪明的特性:这些标示符扩展到呼叫者的位置,当评价一个默认的参数列表时。为了使用这个行为,assert()函数如下定义:
Swift borrows a clever feature from the D language: these identifiers expand to the location of the caller when evaluated in a default argument list. To enable this behavior, the assert() function is defined something like this:
func assert(condition: @autoclosure () -> Bool, _ message: String = "",
file: String = __FILE__, line: Int = __LINE__) {
#if DEBUG
if !condition() {
println("assertion failed at \(file):\(line): \(message)")
abort()
}
#endif
}
第二个参数是一个可选的字符串,可以指定,第三个和第四个争论是调用者上下文中的位置。这允许assert()捡起默认调用的源位置,如果想要在assert的头部定义自己的抽象表现,可以通过上下文的位置。作为一个普通例子,你可以定义一个函数,并声明这样:
The second parameter to the Swift assert() function is an optional string that you can specify, and the third and forth arguments are defaulted to be the position in the caller’s context. This allows assert()to pick up the source location of the caller by default, and if you want to define your own abstractions on top of assert, you can pass down locations from its caller. As a trivial example, you could define a function that logs and asserts like this:
func logAndAssert(condition: @autoclosure () -> Bool, _ message: StaticString = "",
file: StaticString = __FILE__, line: UWord = __LINE__) {
logMessage(message)
assert(condition, message, file: file, line: line)
}
这个合理的传播了logAndAssert()调用者的文件/线位置到assert()的实现。注意StaticString,如上代码所示,是一个简单的类似字符串类型的使用。例如一个产生__FILE__,没有内存管理的开销。
This properly propagates the file/line location of the logAndAssert() caller down to the implementation of assert(). Note that StaticString, as shown in the code above, is a simple String-like type used to store a string literal, such as one produced by __FILE__, with no memory-management overhead.
除了有用的assert(),这个功能用于Swift 实现更高层的XCTest框架,对于自己的库也是很有用的。
In addition to being useful for assert(), this functionality is used in the Swift implementation of the higher-level XCTest framework, and may be useful for your own libraries as well.
-
Sep 9, 2014
2014年6月2日,在WWDC,Swift 团队最后展示了他们多年的工作成果。这是对于我们、开发者来说,是一个令人兴奋的大日子。这天,我们达到了第二大里程碑。
On June 2, 2014 at WWDC, the Swift team finally showed you what we had been working on for years. That was a big day with lots of excitement, for us and for developers around the world. Today, we’ve reached the second giant milestone:
- Swift1.0已经成熟Swift version 1.0 is now GM.
你可以使用Swift提交应用到App Store。不管你的应用中Swift 部分是一个小特性还是完整的项目,现在是时间分享应用给世界了。轮到你和你的作品来激发每个人了。
You can now submit your apps that use Swift to the App Store. Whether your app uses Swift for a small feature or a complete application, now is the time to share your app with the world. It’s your turn to excite everyone with your new creations.
-
Swift用于OS XSwift for OS X
今天是Swift在iOS 中的大日子。也是在Mac 中的。Swift 用于Mac 操作系统需要Yosemite 的SDK,当Yosemite这个秋天启程,Swift 将会Mac 中的大日子。这意味着,你可以使用Swift 和Xcod6.1版本为Mac 开发应用。
Today is the GM date for Swift on iOS. We have one more GM date to go for Mac. Swift for OS X currently requires the SDK for OS X Yosemite, and when Yosemite ships later this fall, Swift will also be GM on the Mac. In the meantime, you can keep developing your Mac apps with Swift by downloading the beta of Xcode 6.1.
你将注意到我们使用“GM”而不是“final”。因为Swift 将会继续增加新的特性,提高表现,重新定义语法。事实上,Xcode6.1中不会有太多变化。以为你的应用嵌入的一个版本的Swift 运行时,将会在将来也能良好的运行。
You’ll notice we’re using the word “GM”, not “final”. That’s because Swift will continue to advance with new features, improved performance, and refined syntax. In fact, you can expect a few improvements to come in Xcode 6.1 in time for the Yosemite launch. Because your apps today embed a version of the Swift GM runtime, they will continue to run well into the future.
-
Sep 3, 2014
在Swift 中,一个模式是一种描述和匹配一系列基于一定规则的值的方式,例如:
-
In Swift, a pattern is a way to describe and match a set of values based on certain rules, such as:
- 所有元组的第一个值为0All tuples whose first value is 0
- 所有数字范围在1到5All numbers in the range 1...5
- 所有类实例为一种特定的类型All class instances of a certain type
在学习游乐场链接如下,包含嵌入式文件和为你的平台做的实验。下载一个互动的经验将会给你一个跳转,到你应用的使用模式。
这个游乐场需要Mavericks或者Yosemite系统和最新的Xcode6。
The learning playground linked below includes embedded documentation and experiments for you to perform. Download it for an interactive experience that will give you a jump start into using patterns in your own apps.
This playground requires the latest beta version of Xcode 6 on OS X Mavericks or OS X Yosemite beta.
-
Aug 26, 2014
本文探讨如何自选帮助来保持在Swift中强类型安全。我们将创建一个OC帮助的Swift 版本。Swift不是真的要用这个文档,但它会是个有趣的例子。
-
This post explores how optionals help preserve strong type safety within Swift. We’re going to create a Swift version of an Objective-C API. Swift doesn’t really need this API, but it makes for a fun example.
在OC中,NSDictionary有一个方法-objectsForKeys:notFoundMarker:使用一个键的数组,返回一个相应的值的数组。在文档中“在返回的数组中对应于[]键输入参数n关键字n个对象”。如果第三个关键不是实际上的字典会怎样?这是notfoundmarker参数的来源。数组中的第三元将是此标记的对象而不是从字典中的值。基础框架还提供这类如果你没有另一个使用:nsnull。
-
In Objective-C, NSDictionary has a method -objectsForKeys:notFoundMarker: that takes an NSArray of keys, and returns an NSArray of corresponding values. From the documentation: “the N-th object in the returned array corresponds to the N-th key in [the input parameter] keys.” What if the third key isn’t actually in the dictionary? That’s where the notFoundMarker parameter comes in. The third element in the array will be this marker object rather than a value from the dictionary. The Foundation framework even provides a class for this case if you don’t have another to use: NSNull.
在Swift 中,字典类型没有一个objectsForKeys等效的方法。在这个练习中,我们会添加一个,来保持普遍使用的“value”在Swift中。使用一个扩展:
-
In Swift, the Dictionary type doesn’t have an objectsForKeys equivalent. For this exercise, we’re going to add one — as valuesForKeys in keeping with the common use of ‘value’ in Swift — using an extension:
extension Dictionary {
func valuesForKeys(keys: [K], notFoundMarker: V) -> [V] {
这是我们在Swift 中的新的实现,不同于OC。在Swift 中,更强的类型限制结果数组来包含一个单一类型的元素——我们不能把NSNull放入一个字符串数组。总之,Swift 提供更好的选择:我们返回一个可选的数组。所有的值都是可选的,而NSNull我们仅使用了nil。
-
This is where our new implementation in Swift will differ from Objective-C. In Swift, the stronger typing restricts the resulting array to contain only a single type of element — we can’t put NSNull in an array of strings. However, Swift gives an even better option: we can return an array of optionals. All our values get wrapped in optionals, and instead of NSNull, we just use nil.
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
var result = [Value?]()
result.reserveCapacity(keys.count)
for key in keys {
result.append(self[key])
}
return result
}
}
注意:一些人会猜测为什么Swift 的字典不用这个文档,或许已经有了想法:
-
NOTE: Some of you may have guessed why a Swift Dictionary doesn’t need this API, and already imagined something like this:
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return keys.map { self[$0] }
}
}
这是This has the exact same effect as the imperative version above, but all of the boilerplate has been wrapped up in the call to map. This is great example why Swift types often have a small API surface area, because it’s so easy to just call map directly.
Now we can try out some examples:
let dict = ["A": "Amir", "B": "Bertha", "C": "Ching"]
dict.valuesForKeys(["A", "C"])
Nested Optionals
Now, what if we asked for the last element of each result?
dict.valuesForKeys(["A", "C"]).last
That’s strange — we have two levels of Optional in the first case, and Optional(nil) in the second case. What’s going on?
Remember the declaration of the last property:
var last: T? { get }
This says that the last property’s type is an Optional version of the array’s element type. In this case, the element type is also optional (String?). So we end up with String??, a doubly-nested optional type.
So what does Optional(nil) mean?
Recall that in Objective-C we were going to use NSNull as a placeholder. The Objective-C version of these three calls looks like this:
[dict valuesForKeys:@[@"A", @"C"] notFoundMarker:[NSNull null]].lastObject
In both the Swift and Objective-C cases, a return value of nil means “the array is empty, therefore there’s no last element.” The return value of Optional(nil) (or in Objective-C NSNull) means “the last element of this array exists, but it represents an absence.” Objective-C has to rely on a placeholder object to do this, but Swift can represent it in the type system.
Providing a Default
To wrap up, what if you did want to provide a default value for anything that wasn’t in the dictionary? Well, that’s easy enough.
extension Dictionary {
func valuesForKeys(keys: [Key], notFoundMarker: Value) -> [Value] {
return self.valuesForKeys(keys).map { $0 ?? notFoundMarker }
}
}
dict.valuesForKeys(["B", "D"], notFoundMarker: "Anonymous")
While Objective-C has to rely on a placeholder object to do this, Swift can represent it in the type system, and provides rich syntactic support for handling optional results.
-
Aug 19, 2014
The response to support for access control in Swift has been extremely positive. However, some developers have been asking, “Why doesn’t Swift have something like protected?” Many other programming languages have an access control option that restricts certain methods from being accessed from anywhere except subclasses.
When designing access control levels in Swift, we considered two main use cases:
- keep private details of a class hidden from the rest of the app
- keep internal details of a framework hidden from the client app
These correspond to private and internal levels of access, respectively.
In contrast, protected conflates access with inheritance, adding an entirely new control axis to reason about. It doesn’t actually offer any real protection, since a subclass can always expose “protected” API through a new public method or property. It doesn’t offer additional optimization opportunities either, since new overrides can come from anywhere. And it’s unnecessarily restrictive — it allows subclasses, but not any of the subclass’s helpers, to access something.
As some developers have pointed out, Apple frameworks do occasionally separate parts of API intended for use by subclasses. Wouldn’t protected be helpful here? Upon inspection, these methods generally fall into one of two groups. First, methods that aren’t really useful outside the subclass, so protection isn’t critical (and recall the helper case above). Second, methods that are designed to be overridden but not called. An example is drawRect(_:), which is certainly used within the UIKit codebase but is not to be called outside UIKit.
It’s also not clear how protected should interact with extensions. Does an extension to a class have access to that class’s protected members? Does an extension to a subclass have access to the superclass’s protected members? Does it make a difference if the extension is declared in the same module as the class?
There was one other influence that led us to the current design: existing practices of Objective-C developers both inside and outside of Apple. Objective-C methods and properties are generally declared in a public header (.h) file, but can also be added in class extensions within the implementation (.m) file. When parts of a public class are intended for use elsewhere within the framework but not outside, developers create a second header file with the class’s “internal” bits. These three levels of access correspond to public, private, and internal in Swift.
Swift provides access control along a single, easy-to-understand axis, unrelated to inheritance. We believe this model is simpler, and provides access control the way it is most often needed: to isolate implementation details to within a class or within a framework. It may be different from what you’ve used before, but we encourage you to try it out.
-
Aug 15, 2014
Types in Swift fall into one of two categories: first, “value types”, where each instance keeps a unique copy of its data, usually defined as a struct, enum, or tuple. The second, “reference types”, where instances share a single copy of the data, and the type is usually defined as a class. In this post we explore the merits of value and reference types, and how to choose between them.
What’s the Difference?
The most basic distinguishing feature of a value type is that copying — the effect of assignment, initialization, and argument passing — creates an independent instance with its own unique copy of its data:
Copying a reference, on the other hand, implicitly creates a shared instance. After a copy, two variables then refer to a single instance of the data, so modifying data in the second variable also affects the original, e.g.:
The Role of Mutation in Safety
One of the primary reasons to choose value types over reference types is the ability to more easily reason about your code. If you always get a unique, copied instance, you can trust that no other part of your app is changing the data under the covers. This is especially helpful in multi-threaded environments where a different thread could alter your data out from under you. This can create nasty bugs that are extremely hard to debug.
Because the difference is defined in terms of what happens when you change data, there’s one case where value and reference types overlap: when instances have no writable data. In the absence of mutation, values and references act exactly the same way.
You may be thinking that it could be valuable, then, to have a case where a class is completely immutable. This would make it easier to use Cocoa NSObject objects, while maintaining the benefits of value semantics. Today, you can write an immutable class in Swift by using only immutable stored properties and avoiding exposing any APIs that can modify state. In fact, many common Cocoa classes, such as NSURL, are designed as immutable classes. However, Swift does not currently provide any language mechanism to enforce class immutability (e.g. on subclasses) the way it enforces immutability for struct and enum.
How to Choose?
So if you want to build a new type, how do you decide which kind to make? When you’re working with Cocoa, many APIs expect subclasses of NSObject, so you have to use a class. For the other cases, here are some guidelines:
Use a value type when:
- Comparing instance data with == makes sense
- You want copies to have independent state
- The data will be used in code across multiple threads
Use a reference type (e.g. use a class) when:
- Comparing instance identity with === makes sense
- You want to create shared, mutable state
In Swift, Array, String, and Dictionary are all value types. They behave much like a simple int value in C, acting as a unique instance of that data. You don’t need to do anything special — such as making an explicit copy — to prevent other code from modifying that data behind your back. Importantly, you can safely pass copies of values across threads without synchronization. In the spirit of improving safety, this model will help you write more predictable code in Swift.
-
Aug 8, 2014
Many people have asked about the Balloons playground we demonstrated when introducing Swift at WWDC. Balloons shows that writing code can be interactive and fun, while presenting several great features of playgrounds. Now you can learn how the special effects were done with this tutorial version of ‘Balloons.playground’, which includes documentation and suggestions for experimentation.
This playground uses new features of SpriteKit and requires the latest beta versions of Xcode 6 and OS X Yosemite.
-
Aug 5, 2014
The boolean Bool type in Swift underlies a lot of primitive functionality, making it an interesting demonstration of how to build a simple type. This post walks through the creation of a new MyBool type designed and implemented to be very similar to the Bool type built into Swift. We hope this walk through the design of a simple Swift type will help you better understand how the language works.
Let’s start with the basic definition. The MyBool type models two different cases, perfect for an enum:
enum MyBool {
case myTrue, myFalse
}
To reduce confusion in this post, we’ve named the cases myTrue and myFalse. We want MyBool() to produce a false value, and can do so by providing an init method:
extension MyBool {
init() { self = .myFalse }
}
Swift enum declarations implicitly scope their enumerators within their body, allowing us to refer to MyBool.myFalse and even .myFalse when a contextual type is available. However, we want our type to work with the primitive true and false literal keywords. To make this work, we can make MyBoolconform to the BooleanLiteralConvertible protocol like this:
extension MyBool : BooleanLiteralConvertible {
static func convertFromBooleanLiteral(value: Bool) -> MyBool {
return value ? myTrue : myFalse
}
}
With this set up, we have our basic type, but we still can’t do much with it. Booleans need to be testable within an if condition. Swift models this with the BooleanType protocol, which allows any type to be used as a logical condition:
extension MyBool : BooleanType {
var boolValue: Bool {
switch self {
case .myTrue: return true
case .myFalse: return false
}
}
}
We also want anything that conforms to BooleanType to be castable to MyBool, so we add:
extension MyBool {
Note that the use of _ in the initializer argument list disables the keyword argument, which allows the MyBool(x) syntax to be used instead of requiring MyBool(v: x).
Now that we have basic functionality, let’s define some operators to work with it, starting with the ==operator. Simple enums that have no associated data (like MyBool) are automatically made Equatableby the compiler, so no additional code is required. However, you can make arbitrary types equatable by conforming to the Equatable protocol and implementing the == operator. If MyBool weren’t already Equatable, this would look like this:
extension MyBool : Equatable {
}
func ==(lhs: MyBool, rhs: MyBool) -> Bool {
switch (lhs, rhs) {
case (.myTrue,.myTrue), (.myFalse,.myFalse):
return true
default:
return false
}
}
Here we’re using some simple pattern matching in the switch statement to handle this. Since MyBool is now Equatable, we get a free implementation of the != operator. Lets add binary operations:
func &(lhs: MyBool, rhs: MyBool) -> MyBool {
if lhs {
return rhs
}
return false
}
func |(lhs: MyBool, rhs: MyBool) -> MyBool {
if lhs {
return true
}
return rhs
}
func ^(lhs: MyBool, rhs: MyBool) -> MyBool {
return MyBool(lhs != rhs)
}
With the basic operators in place, we can implement a variety of helpful unary and compound assignment operators as well, for example:
prefix func !(a: MyBool) -> MyBool {
return a ^ true
}
The &= operator takes the left operand as inout because it reads and writes to it, and the effect must be visible to the user of the operator. Swift gives you complete control over mutability of operations on value types like enum and struct.
With this, the simple MyBool type has all of the basic operations and operators. Hopefully this post gives you a few tips that you can apply to your own code when defining higher-level types.
-
Aug 1, 2014
By now, most of you have written a small Swift app or experimented in the playground. You may even have experienced an error after you copied code from a playground into another file and wondered, “What is actually going on? What is the difference between a playground file, and other Swift source files?” This post will explain how Swift deals with the files in your project, and how global data is initialized.
Files in an App
A Swift app is composed of any number of files, each with the functions, classes, and other declarations that make up the app. Most Swift files in your app are order-independent, meaning you can use a type before it is defined, and can even import modules at the bottom of the file (although that is not recommended Swift style.)
However, top-level code is not allowed in most of your Swift source files. For clarity, any executable statement not written within a function body, within a class, or otherwise encapsulated is considered top-level. We have this rule because if top-level code were allowed in all your files, it would be hard to determine where to start the program.
Playgrounds, REPL, and Top-Level Code
You may be wondering why the code below works perfectly in a playground. This example isn’t encapsulated in anything, so it must be top-level code:
println("Hello world")
The above single-line program works — with no additional code at all — because playground files do support the execution of top-level code. Code within a playground file is order-dependent, run in top-down lexical order. For example, you can’t use a type before you define it. Of course, Swift playground files can also define functions, classes, and any other legal Swift code, but they don’t need to. This makes it easy to learn the Swift language or try a new API without writing a lot of code to get started.
In addition to playgrounds, top-level code can also be run in the REPL (Read-Eval-Print-Loop) or when launching Swift files as scripts. To use Swift for scripting, you can use shebang-style launching by starting your Swift file with “#!/usr/bin/xcrun swift” or type “xcrun swift myFile.swift” within Terminal.
Application Entry Points and “main.swift”
You’ll notice that earlier we said top-level code isn’t allowed in most of your app’s source files. The exception is a special file named “main.swift”, which behaves much like a playground file, but is built with your app’s source code. The “main.swift” file can contain top-level code, and the order-dependent rules apply as well. In effect, the first line of code to run in “main.swift” is implicitly defined as the main entrypoint for the program. This allows the minimal Swift program to be a single line — as long as that line is in “main.swift”.
In Xcode, Mac templates default to including a “main.swift” file, but for iOS apps the default for new iOS project templates is to add @UIApplicationMain to a regular Swift file. This causes the compiler to synthesize a main entry point for your iOS app, and eliminates the need for a “main.swift” file.
Alternatively, you can link in an implementation of main written in Objective-C, common when incrementally migrating projects from Objective-C to Swift.
Global Variables
Given how Swift determines where to start executing an app, how should global variables work? In the following line of code, when should the initializer run?
var someGlobal = foo()
In a single-file program, code is executed top-down, similar to the behavior of variables within a function. Pretty simple. The answer for complex apps is less obvious, and we considered three different options:
- Restrict initializers of global variables to be simple constant expressions, as C does.
- Allow any initializer, run as a static constructor at app load time, as C++ does.
- Initialize lazily, run the initializer for a global the first time it is referenced, similar to Java.
The first approach was ruled out because Swift doesn’t need constant expressions like C does. In Swift, constants are generally implemented as (inlined) function calls. And there are good reasons to use complex initializers, e.g. to set up singletons or allocate a dictionary.
The second approach was ruled out because it is bad for the performance of large systems, as all of the initializers in all the files must run before the application starts up. This is also unpredictable, as the order of initialization in different files is unspecified.
Swift uses the third approach, which is the best of all worlds: it allows custom initializers, startup time in Swift scales cleanly with no global initializers to slow it down, and the order of execution is completely predictable.
The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.
Summary
Swift is designed to make it easy to experiment in a playground or to quickly build a script. A complete program can be a single line of code. Of course, Swift was also designed to scale to the most complex apps you can dream up. With “main.swift” you can take complete control over initialization or you can let @UIApplicationMain do the startup work for you on iOS.
-
Jul 28, 2014
Objective-C and C APIs often require the use of pointers. Data types in Swift are designed to feel natural when working with pointer-based Cocoa APIs, and Swift automatically handles several of the most common use cases for pointers as arguments. In this post we’ll look at how pointer parameters in C can be used with the variables, arrays, and strings in Swift.
Pointers as In/Out Parameters
C and Objective-C don’t support multiple return values, so Cocoa APIs frequently use pointers as a way of passing additional data in and out of functions. Swift allows pointer parameters to be treated like inout parameters, so you can pass a reference to a var as a pointer argument by using the same &syntax. For instance, UIColor’s getRed(_:green:blue:alpha:) method takes four CGFloat* pointers to receive the components of the color. We can use & to collect these components into local variables:
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
Another common case is the Cocoa NSError idiom. Many methods take an NSError** parameter to save an error in case of failure. For instance, we can list the contents of a directory using NSFileManager’s contentsOfDirectoryAtPath(_:error:) method, saving the potential error directly to an NSError? variable:
var maybeError: NSError?
if let contents = NSFileManager.defaultManager()
.contentsOfDirectoryAtPath("/usr/bin", error: &maybeError) {
For safety, Swift requires the variables to be initialized before being passed with &. This is because it cannot know whether the method being called tries to read from a pointer before writing to it.
Pointers as Array Parameters
Pointers are deeply intertwined with arrays in C, and Swift facilitates working with array-based C APIs by allowing Array to be used as a pointer argument. An immutable array value can be passed directly as a const pointer, and a mutable array can be passed as a non-const pointer argument using the &operator, just like an inout parameter. For instance, we can add two arrays a and b using the vDSP_vadd function from the Accelerate framework, writing the result to a third result array:
import Accelerate
let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
vDSP_vadd(a, 1, b, 1, &result, 1, 4)
Pointers as String Parameters
C uses const char* pointers as the primary way to pass around strings. A Swift String can be used as a const char* pointer, which will pass the function a pointer to a null-terminated, UTF–8-encoded representation of the string. For instance, we can pass strings directly to standard C and POSIX library functions:
puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0o666)
if fd < 0 {
perror("could not open /tmp/scratch.txt")
} else {
let text = "Hello World"
write(fd, text, strlen(text))
close(fd)
}
Safety with Pointer Argument Conversions
Swift works hard to make interaction with C pointers convenient, because of their pervasiveness within Cocoa, while providing some level of safety. However, interaction with C pointers is inherently unsafe compared to your other Swift code, so care must be taken. In particular:
- These conversions cannot safely be used if the callee saves the pointer value for use after it returns. The pointer that results from these conversions is only guaranteed to be valid for the duration of a call. Even if you pass the same variable, array, or string as multiple pointer arguments, you could receive a different pointer each time. An exception to this is global or static stored variables. You can safely use the address of a global variable as a persistent unique pointer value, e.g.: as a KVO context parameter.
- Bounds checking is not enforced when a pointer to an Array or String is passed. A C-based API can’t grow the array or string, so you must ensure that the array or string is of the correct size before passing it over to the C-based API.
If you need to work with pointer-based APIs that don’t follow these guidelines, or you need to override Cocoa methods that accept pointer parameters, then you can work directly with raw memory in Swift using unsafe pointers. We’ll look at a more advanced case in a future post.
-
Jul 23, 2014
In Xcode 6 beta 4, Swift adds support for access control. This gives you complete control over what part of the code is accessible within a single file, available across your project, or made public as API for anyone that imports your framework. The three access levels included in this release are:
- private entities are available only from within the source file where they are defined.
- internal entities are available to the entire module that includes the definition (e.g. an app or framework target).
- public entities are intended for use as API, and can be accessed by any file that imports the module, e.g. as a framework used in several of your projects.
By default, all entities have internal access. This allows application developers to largely ignore access control, and most Swift code already written will continue to work without change. Your framework code does need to be updated to define public API, giving you total control of the exposed interface your framework provides.
The private access level is the most restrictive, and makes it easy to hide implementation details from other source files. By properly structuring your code, you can safely use features like extensions and top-level functions without exposing that code to the rest of your project.
Developers building frameworks to be used across their projects need to mark their API as public. While distribution and use of 3rd-party binary frameworks is not recommended (as mentioned in a previous blog post), Swift supports construction and distribution of frameworks in source form.
In addition to allowing access specification for an entire declaration, Swift allows the get of a property to be more accessible than its set. Here is an example class that is part of a framework:
public class ListItem {
When mixing Objective-C and Swift, because the generated header for a framework is part of the framework’s public Objective-C interface, only declarations marked public appear in the generated header for a Swift framework. For applications, the generated header contains both public and internal declarations.
For more information, The Swift Programming Language and Using Swift with Cocoa and Objective-Cbooks have been updated to cover access control. Read the complete Xcode 6 beta 4 release notes here.
-
Jul 18, 2014
UPDATE: This post has been updated to reflect a change in Xcode 6 beta 5 that renamed @auto_closure to @autoclosure, and LogicValue to BooleanType.
When designing |
请发表评论