修改文档名称及顺序,与官方文档保持一致 (#1005)

This commit is contained in:
Licardo
2019-10-11 03:00:57 +08:00
committed by Jie Liang
parent 4dcdd13e74
commit 028a6aa795
56 changed files with 690 additions and 442 deletions

View File

@ -0,0 +1,83 @@
# Access Control 权限控制的黑与白
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
如果您之前没有接触过权限控制,先来听一个小故事:
> 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说。直到有一天,他和学姐小 K 吐槽。
> 学姐听了之后,说:大学集体生活里面,大部分东西都是默认室友可以共用的。如果你不想别人拿,我可以帮你做封印,只要打上 private 标记,它们就看不到你的东西,更加用不了你的东西了。
> 小明说哇靠学姐你还会妖法......
Swift 语言从 Xcode 6 beta 5 版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的 API。
这三种权限分别是:
- #####private 私有的
在哪里写的,就在哪里用。无论是类、变量、常量还是函数,一旦被标记为私有的,就只能在定义他们的源文件里使用,不能为别的文件所用。
- #####internal 内部的
标记为 internal 的代码块在整个应用App bundle或者框架framework的范围内都是可以访问的。
- #####public 公开的
标记为 public 的代码块一般用来建立 API这是最开放的权限使得任何人只要导入这个模块都可以访问使用。
如果要把所有的爱加上一个期限,噢不,是给所有的代码块都标记上权限,不累死才怪。还好 swift 里面所有代码实体的默认权限,都是最常用的 internal。所以当你开发自己的 App 时,可能完全不用管权限控制的事情。
但当你需要写一个公开 API 的时候,就必须对里面的代码块进行“隐身对其可见”的 public 标记,要么其他人是用不到的。
Private私有级别的权限最严格它可以用来隐藏某些功能的细节实现方式。合理构筑你的代码你就可以安全地使用 extension 和高级功能,又不把它们暴露给项目内的其他文件。
除了可以给整个声明设权限Swift 还允许大家在需要的时候把某个属性property的取值权限比赋值权限设得更加开放。
#####举个例子:
```swift
public class ListItem {
// ListItem 这个类,有两个公开的属性
public var text: String
public var isComplete: Bool
// 下面的代码表示把变量 UUID 的赋值权限设为 private对整个 app 可读,但值只能在本文件里写入
private(set) var UUID: NSUUID
public init(text: String, completed: Bool, UUID: NSUUID) {
self.text = text
self.isComplete = completed
self.UUID = UUID
}
// 这段没有特别标记权限,因此属于默认的 internal 级别。在框架目标内可用,但对于其他目标不可用
func refreshIdentity() {
self.UUID = NSUUID()
}
public override func isEqual(object: AnyObject?) -> Bool {
if let item = object as? ListItem {
return self.UUID == item.UUID
}
return false
}
}
```
当我们使用 Objective-C 和 Swift 混合开发时,需要注意:
- 如果你在写的是一个应用Xcode 会生成一个头文件来保证两者的可互访性,而这个生成的头文件会包含 public 和 internal 级别的声明。
- 如果你的最终产品是一个 Swift 框架,头文件里只会出现标记为 public 级别的声明。(因为框架的头文件,属于公开的 Objective-C 接口的一部分,只有 public 部分对 Objective-C 可用。)
虽然 Swift 不推荐大家传播和使用第三方的框架,但对于建立和分享源文件形式的框架是支持的。对于需要写框架,方便应用与多个项目的开发者来说,要记得把 API 标记为 public 级别。
如果您想了解更多关于权限控制的内容可以查看苹果官方最新的《The Swift Language》和《Using Swift with Cocoa and Objective-C》指南
这两本指南在 iBooks 里面可以下载更新喔。
本文由翻译自 Apple Swift Blog https://developer.apple.com/swift/blog/?id=5

View File

@ -0,0 +1,300 @@
# 造个类型不是梦-白话 Swift 类型创建
-----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
本页包含内容:
- [自定义原型](#prototype)
- [实现默认值](#imp-default)
- [支持基本布尔型初始化](#init-by-bool)
- [支持 Bool 类型判断](#condition-by-bool)
- [支持兼容各们各派的类型](#support-all-type)
- [完善 OCBool 的布尔基因体系](#make-up-type)
小伙伴们Swift 中的 Bool 类型有着非常重要的语法功能,并支撑起了整个 Swift 体系中的逻辑判断体系,经过老码的研究和学习, Bool 类型本身其实是对基础 Boolean 类型封装,小伙伴们可能咬着手指头问老码,怎么一会 Bool 类型,一会 Boolean 类型,其区别在于,前者是基于枚举的组合类型,而后者则是基本类型,只有两种 true 和 false。
####自定义原型 {#prefix-expressions}
接下老码根据 Bool 的思想来创建一个 OCBool 类型,来让小伙伴们了解一下 Swift 中到底是怎么玩儿的。
来我们先看一下 OCBool 的定义。
#####代码示例如下:
```swift
enum OCBool{
case ocTrue
case ocFalse
}
```
#####注意:
- 代码中第2行和第3行可以合并到一行写如苹果官方 Blog 所写的一样
- 代码中命名需要注意OCBool 是类型名,所以首字母必须大写,而 case 中的 ocTrue 和 ocFalse 是小类型则需要首字母小写。
####实现默认值 {#imp-default}
我们给了一个漂亮的定义不过按照传统语言的经验Bool 值默认情况下是假, 所以我们的 OCBool 也应该如此,我们使用类型扩展技术增加这个默认特性:
```swift
extension OCBool{
init(){
self =.ocFalse
}
}
```
#####注意:
- 代码中第1行extension 关键字,非常强大,小伙伴们可以通过此创造出许多好玩的东西,建议各位去 Github 上看一个名为“Swiftz”的项目它将扩展用到了极致。
- 代码中第3行self = .ocFalse 语法,刚入门的小伙伴们很迷糊,为什么会有奇怪的点语法,因为大牛 Chris 在 Swift 中增加了类型智能推断功能,在苹果 Blog 中提到了“Context”概念就是这个意思因为这行语句是在枚举 OCBool 中的,其上下文就是 OCBool 的定义体,编译器当然知道.ocFalse 就是 OCBool.ocFalse 了,所以这里直接点语法,非常整齐。
现在我们可以使用如下方法使用这个 Bool 类型。
#####代码示例如下:
```swift
var result:OCBool = OCBool()
var result1:OCBool = .ocTrue
```
####支持基本布尔型初始化 {#init-by-bool}
正如上述代码所述,我们只能通过类型或者枚举项目赋值,这是组合类型的用法,但是编码的日子里,我们总是希望和 truefalse 直接打交道,也就是说,我们希望这么做,
代码示例如下:
```swift
var isSuccess:OCBool = true
```
如果小伙伴们直接这么用,则会出现如下错误:
```
/Users/tyrion-OldCoder/Documents/Learning/BoolType/BoolType/main.swift:30:24: Type 'OCBool' does not conform to protocol 'BooleanLiteralConvertible'
```
编译器咆哮的原因是,我们的类型没有遵从“布尔字面量转换协议”,接下来修正这个问题,
#####代码示例如下:
```swift
import Foundation
println("Hello, World!")
enum OCBool{
case ocTrue
case ocFalse
}
extension OCBool: BooleanLiteralConvertible{
static func convertFromBooleanLiteral( value: Bool) ->OCBool{
return value ? ocTrue : ocFalse
}
}
var isSuccess:OCBool = true
```
#####注意:
- 代码中的第11行是重点我的类型 OCBool 支持了 BooleanLiteralConvertible 协议,这个协到底是干什么的呢,小伙伴们在 Xcode 代码编辑器,按住 Command 键然后点击第11行中的 BooleanLiteralConvertible 协议名,则会进入它的定义,
#####其定义如下:
```swift
protocol BooleanLiteralConvertible {
typealias BooleanLiteralType
class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
}
```
- 这个定义中有个类方法 convertFromBooleanLiteral它的参数为 BooleanLiteralType 类型,也就是我传入的 Bool 类型, 且返回值为实现这个协议的类型本身,在我们的 OCBool 类型中,其返回值就是 OCBool 本身。经过这个定义,我们可以直接对 OCBool 类型直接进行布尔字面量初始化了。
####支持 Bool 类型判断 {#condition-by-bool}
小伙伴们不安分, 肯定想着我怎么用它实现逻辑判断,所以如果你这么写,
#####代码示例如下:
```swift
var isSuccess:OCBool = true
if isSuccess {
println("老码请你吃火锅!")
}
```
你永远吃不到老码的火锅,因为这里编译器会咆哮:
```
/Users/tyrion-OldCoder/Documents/Learning/BoolType/BoolType/main.swift:27:4: Type 'OCBool' does not conform to protocol 'LogicValue'
```
OCBool 现在只能用 bool 类型初始化,而不能直接返回 bool 型,小火把们还记得在《老码说编程之白话 Swift 江湖》中,老码多次提到,妈妈再也不担心我们 if a = 1{}的写法了, 因为等号不支持值返回了, 所以在 if 判断是后面的条件必须有返回值OCBool 没有,所以编译器哭了。我们解决这个问题。
#####代码示例如下:
```swift
import Foundation
println("Hello, World!")
enum OCBool{
case ocTrue
case ocFalse
}
extension OCBool: BooleanLiteralConvertible{
static func convertFromBooleanLiteral( value: Bool) ->OCBool{
return value ? ocTrue : ocFalse
}
}
extension OCBool: LogicValue{
func getLogicValue() ->Bool {
var boolValue: Bool{
switch self{
case .ocTrue:
return true
case .ocFalse:
return false
}
}
return boolValue
}
}
var isSuccess:OCBool = true
if isSuccess {
println("老码请你吃火锅!")
}
```
####运行结果如下:
```
Hello, World!
老码请你吃火锅!
Program ended with exit code: 0
```
#####注意:
- 如果小伙伴们现在用的是 Beta 版的 Xcode注意苹果官方 Blog 中在代码第17行如果在 Xcode Beta4下是错误的这里的协议是LogicValue 而不是 BooleanVue所以记得看错误提示才是好习惯。
- 注意代码第34行完美支持 if 判断,且输出结果为“老码请你吃火锅”,老码也是说说而已,请不要当真。
####支持兼容各们各派的类型 {#support-all-type}
小伙伴们,江湖风险,门派众多,老码有自己的 OCBool 类型,可能嵩山少林有自己的 SSBool 类型,甚至连郭美美都可能有自己的 MMBool 类型,所以 OCBool 必须能够识别这些类型,这些各门各派的类型,只要支持 LogicValue 协议,就应该可以被识别,看老码怎么做,
#####代码示例如下:
```swift
extension OCBool{
init( _ v: LogicValue )
{
if v.getLogicValue(){
self = .ocTrue
}
else{
self = .ocFalse
}
}
}
var mmResult: Bool = true
var ocResult:OCBool = OCBool(mmResult)
if ocResult {
println("老码没钱,郭美美请你吃火锅!")
}
```
#####代码运行结果如下:
```
Hello, World!
老码没钱,郭美美请你吃火锅!
Program ended with exit code: 0
```
漂亮!我们的 OCBool 类型现在支持了所有的逻辑变量初始化。
#####注意:
- 代码中第2行“_”下横杠的用法这是一个功能强大的小强在此的目的是屏蔽外部参数名所以小伙伴们可以直接var ocResult:OCBool = OCBool(mmResult)而不是var ocResult:OCBool = OCBool(v: mmResult),小伙伴们惊呆了!这个 init 函数中本来就没有外部参数名啊还记得老码在书里说过没Swift 的初始化函数会默认使用内部参数名,作为外部参数名。
####完善 OCBool 的布尔基因体系: {#make-up-type}
小伙伴们bool 类型的价值就是在于各种判断,诸如==!=, &|,^,!,以及各种组合逻辑运算,我们 OCBool 也要具备这些功能,否则就会基因缺陷,且看老码如何实现:
```swift
extension OCBool: Equatable{
}
//支持等值判断运算符
func ==( left: OCBool, right: OCBool )->Bool{
switch (left, right){
case (.ocTrue, .ocTrue):
return true
default:
return false
}
}
//支持位与运算符
func &( left:OCBool, right: OCBool)->OCBool{
if left{
return right
}
else{
return false
}
}
//支持位或运算符
func |( left:OCBool, right: OCBool)->OCBool{
if left{
return true
}
else{
return right
}
}
//支持位异或运算符
func ^( left:OCBool, right: OCBool)->OCBool{
return OCBool( left != right )
}
//支持求反运算符
@prefix func !( a:OCBool )-> OCBool{
return a ^ true
}
//支持组合求与运算符
func &= (inout left:OCBool, right:OCBool ){
left = left & right
}
var isHasMoney:OCBool = true
var isHasWife:OCBool = true
var isHasHealty:OCBool = true
var isHasLover:OCBool = true
isHasMoney != isHasHealty
isHasHealty == isHasMoney
isHasWife ^ isHasLover
isHasWife = !isHasLover
if (isHasMoney | isHasHealty) & isHasHealty{
println("人生赢家,就像老码一样!")
}else
{
println("人生最苦的事事,人死了钱没花了,人生最苦的事是,人活着,钱没了!")
}
```
好了,到这里就到这里了,窗外的雷声叫醒了老码,现在应该去吃饭了,以上老码给大家展示了如果制造一个自己的类型,记得老码的示例是在 Xcode6 Beta4下测试的至于 Beta5的改变还没有涉及小伙伴们要好生练习以后各种自定类型都是基于这个思想。还有这个章节不是老码的原创老码认真的阅读了苹果的官方博客且自己的练习总结如果小伙伴们费了吃奶的劲还是看不懂请找度娘谷歌还是看不懂请到老码官方微博http://weibo.com/u/5241713117咆哮。
本文由翻译自 Apple Swift Blog https://developer.apple.com/swift/blog/?id=8

View File

@ -0,0 +1,16 @@
# WWDC 里面的那个“大炮打气球”
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-](Jame)
![Ballon playground](https://devimages.apple.com.edgekey.net/swift/images/swift-screenshot.jpg)
很多小伙伴说,对 WWDC 上介绍 Swift 语言时,演示的那个“大炮打气球”的 Ballons 项目很感兴趣。
Ballons 不但展现了 playgrounds 许多很赞的特性,还让我们看到写代码的过程,原来可以这么互动,这么好玩。
现在你可以下载这个[Ballons.playground](https://developer.apple.com/swift/blog/downloads/Balloons.zip)的教学版本,学习这些有趣的效果是怎么实现的。教学版本里除了源文件,还有相关说明文档,我们还出了一些小小的实验题,你可以动手修改代码,然后在右侧马上看到效果。
这个 playground 文件用到了 SpriteKit 的新特性,因此需要最新 beta 版本的 Xcode 6和 Yosemite 系统来支持它运行。
本文由翻译自 Apple Swift Blog 的博文:[Ballons](https://developer.apple.com/swift/blog/?id=9)

View File

@ -0,0 +1,79 @@
# Swift 与 C 语言指针友好合作
-----------------
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
本页包含内容:
- [用以输入/输出的参数指针](#inout-para-pointer)
- [作为数组使用的参数指针](#array-as-para-pointer)
- [用作字符串参数的指针](#string-as-para-pointer)
- [指针参数转换的安全性](#security-of-pointer-cast)
Objective-C 和 C 的 API 常常会需要用到指针。Swift 中的数据类型都原生支持基于指针的 Cocoa API不仅如此Swift 会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中,我们将着眼于在 Swift 中让 C 语言指针与变量、数组和字符串共同工作。
####用以输入/输出的参数指针
C 和 Objective-C 并不支持多返回值,所以 Cocoa API 中常常将指针作为一种在方法间传递额外数据的方式。Swift 允许指针被当作 `inout` 参数使用,所以你可以用符号 `&` 将对一个变量的引用作为指针参数传递。举例来说:`UIColor` 中的 `getRed(_:green:blue:alpha:)` 方法需要四个 `CGFloat*` 指针来接收颜色的组成信息,我们使用 `&` 来将这些组成信息捕获为本地变量:
```swift
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
```
另一种常见的情况是 Cocoa 中 `NSError` 的习惯用法。许多方法会使用一个 `NSError**` 参数来储存可能的错误的信息。举例来说:我们用 `NSFileManager``contentOfDirectoryAtPath(_:error:)` 方法来将目录下的内容列表,并将潜在的错误指向一个 `NSError?` 变量:
```swift
var maybeError: NSError?
if let contents = NSFileManager.defaultManager()
.contentsOfDirectoryAtPath("/usr/bin", error: &maybeError) {
// Work with the directory contents
} else if let error = maybeError {
// Handle the error
}
```
为了安全性Swift 要求被使用 `&` 传递的变量已经初始化。因为无法确定这个方法会不会在写入数据前尝试从指针中读取数据。
####作为数组使用的参数指针
在 C 语言中,数组和指针的联系十分紧密,而 Swift 允许数组能够作为指针使用,从而与基于数组的 C 语言 API 协同工作更加简单。一个固定的数组可以使用一个常量指针直接传递,一个变化的数组可以用 `&` 运算符将一个非常量指针传递。就和输入/输出参数指针一样。举例来说:我们可以用 Accelerate 框架中的 `vDSP_vadd` 方法让两个数组 `a``b` 相加,并将结果写入第三个数组 `result`
```swift
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)
// result now contains [1.5, 2.25, 3.125, 4.0625]
```
## 用作字符串参数的指针
C 语言中用 `cont char*` 指针来作为传递字符串的基本方式。Swift 中的 `String` 可以被当作一个无限长度 UTF-8编码的 `const char*` 指针来传递给方法。举例来说:我们可以直接传递一个字符串给一个标准 C 和 POSIX 库方法
```swift
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)
}
```
## 指针参数转换的安全性
Swift 很努力地使与 C 语言指针的交互更加便利,因为它们广泛地存在于 Cocoa 之中,同时保持一定的安全性。然而,相比你的其他 Swift 代码与 C 语言的指针交互具有潜在的不安全性,所以务必要小心使用。其中特别要注意:
- 如果被调用者为了在其返回值之后再次使用而保存了 C 指针的数据,那么这些转换使用起来并不安全。转换后的指针仅在调用期间保证有效。甚至你将同样的变量、数组或字符串作为多指针参数再次传递,你每次都会收到一个不同的指针。这个异常将全局或静态地储存为变量。你可以安全地将这段地址当作永久唯一的指针使用。例如:作为一个 KVO 上下文参数使用的时候。
- 当指针类型为 `Array``String` 时,溢出检查不是强制进行的。 基于 C 语言的 API 无法增加数组和字符串大小,所以在你将其传递到基于 C 语言的 API 之前,你必须确保数组或字符的大小正确。
如果你需要使用基于指针的 API 时没有遵守以上指导,或是你重写了接受指针参数的 Cocoa 方法,于是你可以在 Swift 中直接用不安全的指针来使用未经处理的内存。在未来的文章中我们将着眼于更加高级的情况。

View File

@ -0,0 +1,72 @@
# Swift 里的值类型与引用类型
-----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
本页包含内容:
- [值类型与引用类型的区别](#difference-two)
- [Mutation修改在安全中扮演的角色](#act-in=mutation)
- [如何选择类型](#how-to-choose)
### Swift 里面的类型分为两种:
* **值类型Value Types**:每个实例都保留了一分独有的数据拷贝,一般以结构体 `struct`` 枚举enum` 或者 `元组tuple`的形式出现。
* **引用类型Reference Type**:每个实例共享同一份数据来源,一般以 `类class`的形式出现。
在这篇博文里面,我们会介绍两种类型各自的优点,以及应该怎么选择使用。
#### 值类型与引用类型的区别 {#difference-two}
值类型和引用类型最基本的分别在复制之后的结果。当一个值类型被复制的时候,相当于创造了一个完全独立的实例,这个实例保有属于自己的独有数据,数据不会受到其他实例的数据变化影响:
```swift
// 下面是一个值类型的例子
struct S { var data: Int = -1 }
var a = S()
var b = a // b 是 a 的拷贝
a.data = 42 // 更改 a 的数据b 的不受影响
println("\(a.data), \(b.data)") // 输出结果 "42, -1"
```
值类型就好像身份证复印件一样,复印出来之后,修改原件上面的内容,复印件上的内容不会变。
另一方面,复制一个引用类型的时候,实际上是默默地创造了一个共享的实例分身,两者是共用一套数据。因此修改其中任何一个实例的数据,也会影响到另外那个。
```swift
// 下面是一个引用类型的例子
class C { var data: Int = -1 }
var x = C()
var y = x // y 是 x 的拷贝
x.data = 42 // 更改 x 的数据,等于同时修改了 y
println("\(x.data), \(y.data)") // 输出结果 "42, 42"
```
#### Mutation修改在安全中扮演的角色 {#act-in=mutation}
值类型较引用类型来说,会让你更容易在大量代码中理清状况。如果你总是得到一个独立的拷贝出来的实例,你就可以放心它不会被你 app 里面的其他部分代码默默地修改。这在多线程的环境里面是尤为重要的,因为另外一个线程可能会在暗地里修改你的数据。因此可能会造成严重的程序错误,这在调试过程中非常难以排除。
由于差别主要在于修改数据的后果,那么当实例的数据只读,不存在需要更改的情况下,用哪种类型都是没有分别的。
你可能在想,有的时候我可能也需要一个完全不变的类。这样使用 `Cocoa NSObject` 对象的时候会比较容易,又可以保留值语义的好处。在今天,你可以通过只使用不可变的存储属性,和避开任何可以修改状态的 API用 Swift 写出一个不可变类 `immutable class`。实际上,很多基本的 Cocoa 类,例如 `NSURL`都是设计成不可变类的。然而Swift 语言目前只强制 `struct``enum` 这种值类型的不可变性,对类这种引用类型则没有。(例如还不支持强制将子类的限制为不可变类)
#### 如何选择类型? {#how-to-choose}
所以当我们想要建立一个新的类型的时候,怎么决定用值类型还是引用类型呢?当你使用 Cocoa 框架的时候,很多 API 都要通过 NSObject 的子类使用,所以这时候必须要用到引用类型 class。在其他情况下有下面几个准则
* **什么时候该用值类型**
* 要用==运算符来比较实例的数据时
* 你希望那个实例的拷贝能保持独立的状态时
* 数据会被多个线程使用时
* **什么时候该用引用类型class**
* 要用==运算符来比较实例身份的时候
* 你希望有创建一个共享的、可变对象的时候
在 Swift 里面数组Array、字符串String、字典Dictionary都属于值类型。它们就像 C 语言里面简单的 int 值,是一个个独立的数据个体。你不需要花任何功夫来防范其他代码在暗地里修改它们。更重要的是,你可以在线程之间安全的传递变量,而不需要特地去同步。在 Swift 高安全性的精神下,这个模式会帮助你用 Swift 写出更可控的代码。
-----------------
本章节不是老码的原创,老码认真的阅读了苹果的官方博客,且自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌,还是看不懂请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自 Apple Swift Blog [Value and Reference Types](https://developer.apple.com/swift/blog/?id=10)

View File

@ -0,0 +1,33 @@
# 访问控制和 protected
-----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
原文再续,书折第一回。
很多其他编程语言都有一种”protected“设定可以限制某些类方法只能被它的子类所使用。
Swift 支持了访问控制后,大家给我们的反馈都很不错。而有的开发者问我们:“为什么 Swift 没有类似 protected 的选项?”
**当我们在设计 Swift 访问控制的不同等级时,我们认为有两种主要场景:**
* 在一个 APP 里:隐藏某个类的私密细节。
* 在一个开源框架里:不让导入这个框架的 APP随便接触框架的内部实现细节。
上面的两种常见情况,对应着 private 和 internal 这两个等级。
而 protected 相当于把访问控制和继承特性混在一起,把访问控制的等级设定增加了一个维度,使之复杂化。即使设定了 protected子类还是可以通过新的公开方法、新的属性来接触到所谓“protected”了的 API。另一方面我们可以在各种地方重写一个方法所谓的保护却没有提供优化机制。这种设定往往在做不必要的限制 一 protected 允许了子类,但又禁止所有其他别的类(包括那些帮助子类实现某些功能的类)接触父类的成员。
有的开发者指出apple 的框架有时候也会把给子类用的 API 分隔出来。这时候 protected 不就有用了吗?我们研究后发现,这些方法一般属于下面两种情况:一是这些方法对子类以外的类没啥用,所以不需要严格保护(例如上面说的协助实现某些功能的类)。二是这些方法就是设计出来被重写,而不是直接用的。举个例子,`drawRect(_:)` 就是在 UIKit 基础上使用的方法,但它不能在 UIKit 以外应用。
除此之外,如果有了 protected它要怎么样和 extension 相互作用呢?一个类的 extension 能接触它的 protected 成员吗?一个子类的 extension 可以接触父类的 protected 成员吗extension 声明的位置对访问控制等级有没有影响呢?(复杂到要哭了是不是?)
对访问控制的设计,也依循了 ObjectiveC 开发者(包括 apple 内外的的常规做法。ObjectiveC 方法和属性一般在.h 头文件里声明,但也可以写在.m 实现文件里。假如有一个公开的类,想把里面某些部分设为只有框架内可以获取时,开发者一般会创建另一个头文件给内部使用。以上三种访问级别,就对应了 Swift 里面的 publicprivate 和 internal。
Swift 的访问控制等级和继承无关,是单维度、非常清楚明了的。我们认为这样的模式更简洁,同时满足了最主要的需求:将一个类、或一个框架的实现细节隔离保护起来。这可能和你以前用过的不同,但我们鼓励你试试看。
-----------------
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自 Apple Swift Blog [Access Control and Protected](原文地址https://developer.apple.com/swift/blog/?id=11)

View File

@ -0,0 +1,136 @@
# 可选类型完美解决占位问题
-----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Ayra](http://weibo.com/littlekok/)
本页包含内容:
- [为 Dictionary 增加 objectsForKeys 函数](#add-function)
- [Swift 中更简便的方法](##easy-function)
- [内嵌可选类型](#nested-optional)
- [提供一个默认值](#provide-default)
可选类型是 Swift 中新引入的,功能很强大。在这篇博文里讨论的,是在 Swift 里,如何通过可选类型来保证强类型的安全性。作为例子,我们来创建一个 Objective-C API 的 Swift 版本,但实际上 Swift 本身并不需要这样的 API。
#### 为 Dictionary 增加 objectsForKeys 函数 {#add-function}
在 Objective-C 中,`NSDictionary` 有一个方法 `-objectsForKeys:NoFoundMarker:`, 这个方法需要一个 `NSArray` 数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:“返回数组中的第 N 个值,
和输入数组中的第 N 个值相对应”,那如果有某个键值在字典里不存在呢?于是就有了 `notFoundMarker` 作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个 `notFoundMarker`,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用 `notFoundMarker` 作为占位符,因为这个对象不能直接使用,所以在 Foundation 框架中有个专门的类处理这个情况:`NSNull`
在 Swift 中,`Dictionary` 类没有类似 `objectsForKeys` 的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用 `extension` 来实现:
```swift
extension Dictionary{
func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
//具体实现代码后面会写到
}
}
```
以上就是我们实现的 Swift 版本,这个和 Objective-C 版本有很大区别。在 Swift 中,因为其强类型的原因限制了返回的结果数组只能包含单一类型的元素,所以我们不能放 `NSNull` 在字符串数组中但是Swift 有更好的选择,我们可以返回一个可选类型数据。我们所有的值都封包在可选类型中,而不是 `NSNull`, 我们只用 `nil` 就可以了。
```swift
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 中更简便的方法 {#easy-function}
小伙伴们可能会问,为什么 Swift 中不需要实现这么一个 API 呢?其实其有更简单的实现,如下面代码所示:
```swift
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return keys.map { self[$0] }
}
}
```
上述方式实现的功能和最开始的方法实现的功能相同,虽然核心的功能是封装了 `map` 的调用,这个例子也说明了为什么 Swift 没有提供轻量级的 API 接口,因为小伙伴们简单的调用 `map` 就可以实现。
接下来,我们实验几个例子:
```swift
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"])
//结果为:[Optional(2), Optional(5)]
var t = dict.valuesForKeys(["3", "9"])
// 结果为:[Optional(3), nil]
t = dic.valuesForKeys([])
//结果为:[]
```
#### 内嵌可选类型 {#nested-optional}
现在,如果我们为每一个结果调用 `last` 方法,看下结果如何?
```swift
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"]).last //结果为Optional(Optional(5))
// Optional(Optional("Ching"))
var t = dict.valuesForKeys(["3", "9"]).last
// 结果为Optional(nil)
var t = dict.valuesForKeys([]).last
// 结果为nil
```
小伙伴们立马迷糊了,为什么会出现两层包含的可选类型呢?,特别对第二种情况的 `Optional(nil)`,这是什么节奏?
我们回过头看看 `last` 属性的定义:
```swift
var last:T? { get }
```
很明显 `last` 属性的类型是数组元素类型的可选类型,这种情况下,因为元素类型是 `(String?)`,那么再结合返回的类型,于是其结果就是 `String??` 了,这就是所谓的嵌套可选类型。但嵌套可选类型本质是什么意思呢?
如果在 Objective-C 中重新调用上述方法,我们将使用 `NSNull` 作为占位符Objective-C 的调用语法如下所示:
```swift
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
// 5
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil
```
不管是 Swift 版本还是 Objective-C 版本,返回值为 `nil` 都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是 `Optional(nil)` 或者 Objective-C 中的 `NSNull` 都表示数组中的最后一个元素存在,但是元素的内容是空的。在 Objective-C 中只能借助 `NSNull` 作为占位符来达到这个目的,但是 Swift 却可以语言系统类型的角度的实现。
#### 提供一个默认值 {#provide-default}
进一步封装,如果我字典中的某个或某些元素不存在,我们想提供一个默认值怎么办呢?实现方法很简单:
```swift
extension Dictionary {
func valuesForKeys( keys:[Key], notFoundMarker: Value)->[Value]{
return self.valueForKeys(kes).map{ $0 ?? notFoundMarker }
}
}
```
```swift
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")
```
和 Objective-C 相比,其需要占位符来达到占位的目的,但是 Swift 却已经从语言类型系统的层面原生的支持了这种用法,同时提供了丰富的语法功能。这就是 Swift 可选类型的强大之处。同时注意上述例子中用到了空合运算符 `??`
-----------------
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自 Apple Swift Blog [Optionals Case Study: valuesForKeys](https://developer.apple.com/swift/blog/?id=12)