The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// : Playground - noun: a place where people can play

import UIKit

//: 继承(Inheritance)

//: Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。

//: 子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。


class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// 什么也不做-因为车辆不一定会有噪音
}
}

class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}


class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}


//: 你可以通过把方法,属性或下标标记为final来防止它们被重写,只需要在声明关键字前加上final修饰符即可(例如:final var,final func,final class func,以及final subscript)。


The Swift Programming Language Examples

源码在 GitHub:https://github.com/gewill/The-Swift-Programming-Language-2.1-Examples

写了一段时间的 Swift 后,又来恶补基础知识了。

Playground ->

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// : Playground - noun: a place where people can play

import UIKit

//: 下标(Subscripts)

//: 下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。

struct Matrix {
let rows: Int, columns: Int
var grid: [Double]

init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}

func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}

subscript(row: Int, column: Int) -> Double {
get {
precondition(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}

set {
precondition(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}

var matrix = Matrix(rows: 2, columns: 3)
print(matrix[1, 2])
matrix[1, 1] = 3
print(matrix)

使用代码自动布局,需求还是有的,虽然很习惯了 IB 来做。参看 Programming iOS 9。

一共三个方法:

  • Anchor notation
  • Creating constraints in code
  • Visual format notation

1. Anchor notation

感觉 anchor 一个折中的方案,语法比 constraints 简洁,符合 IB 设计和添加约束的思路。美中不足是仅支持 iOS 9。

The NSLayoutAnchor class is a factory class for creating NSLayoutConstraint objects using a fluent API. Use these constraints to programatically define your layout using Auto Layout.

具体的使用语法都很简单,贴一个书中的 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
let v1 = UIView(frame: CGRectMake(100, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView()
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)

mainview.addSubview(v1)
v1.addSubview(v2)

v2.translatesAutoresizingMaskIntoConstraints = false

var which: Int { return 3 }
switch which {
case 1:
// the old way, and this is the last time I'm going to show this
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Leading,
relatedBy: .Equal,
toItem: v1,
attribute: .Leading,
multiplier: 1, constant: 0)
)
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Trailing,
relatedBy: .Equal,
toItem: v1,
attribute: .Trailing,
multiplier: 1, constant: 0)
)
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Top,
relatedBy: .Equal,
toItem: v1,
attribute: .Top,
multiplier: 1, constant: 0)
)
v2.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1, constant: 10)
)

case 2:
// new API in iOS 9 for making constraints individually
// and we should now be activating constraints, not adding them...
// to a specific view
// whereever possible, activate all the constraints at once
NSLayoutConstraint.activateConstraints([
v2.leadingAnchor.constraintEqualToAnchor(v1.leadingAnchor),
v2.trailingAnchor.constraintEqualToAnchor(v1.trailingAnchor),
v2.topAnchor.constraintEqualToAnchor(v1.topAnchor),
v2.heightAnchor.constraintEqualToConstant(10),
])

case 3:

// NSDictionaryOfVariableBindings(v2,v3) // it's a macro, no macros in Swift

// let d = ["v2":v2,"v3":v3]
// okay, that's boring...
// let's write our own Swift NSDictionaryOfVariableBindings substitute (sort of)
let d = dictionaryOfNames(v1, v2, v3)
NSLayoutConstraint.activateConstraints([
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|[v2]|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[v2(10)]", options: [], metrics: nil, views: d),
].flatten().map { $0 })
default: break
}

func dictionaryOfNames(arr: UIView...) -> [String: UIView] {
var d = [String: UIView]()
for (ix, v) in arr.enumerate() {
d["v\(ix + 1)"] = v
}
return d
}

2. Creating constraints in code

兼容 iOS 8 以下的,但是超级啰嗦。

  • iOS 6 可以全部使用最外面的视图添加约束,下面 Demo 中的:container.addConstraint(s) / removeConstraints
  • iOS 8 直接使用:NSLayoutConstraint.activateConstraints / deactivateConstraints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let container = UIView(frame: self.view.bounds)
container.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.252461163294798)
self.view.addSubview(container)

let aLine = UIView()
aLine.frame = CGRect(x: 0, y: 0, width: 5, height: 5)
aLine.backgroundColor = UIColor(red: 0.6667, green: 0.0742, blue: 0.6667, alpha: 1.0)
container.addSubview(aLine)

aLine.translatesAutoresizingMaskIntoConstraints = false

let i = 3

switch (i) {
case 0:
// MARK:- superview addConstraint
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0))
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0))
container.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0))
aLine.addConstraint(NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20))
case 1:
// MARK:- iOS 6
container.addConstraints([
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20),
])
case 2:
// MARK:- active = true
if #available(iOS 8.0, *) {
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20).active = true
} else {
// Fallback on earlier versions
}

case 3:
// MARK:- NSLayoutConstraint.activateConstraints
if #available(iOS 8.0, *) {
NSLayoutConstraint.activateConstraints([
NSLayoutConstraint(item: aLine, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Trailing, relatedBy: .Equal, toItem: container, attribute: .Trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: aLine, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 20),
])
} else {
// Fallback on earlier versions
}
default:
break
}
}

}


3. Visual format notation

这个语法支持 iOS 6,而且语法最为简洁直观。也是
Programming iOS 9 书中推荐的方案。实际项目尝试了上面两种两种方法后,想要更短的代码量的话,还是 Visual format notation 最为合适。这里也推荐大家,还很容易理解其语法,而且 console debugging 也会优先显示该语法。

1
2
3
4
5
6
7
8
9
10
11
12
<NSLayoutConstraint:0x7f855ad1bb00 H:[UIButton:0x7f855ad1bba0'Button'(46@188)] priority:188>

<NSLayoutConstraint:0x7f855ad1e130 UIButton:0x7f855ad1bba0'Button'.leading == UIView:0x7f855ad1ca20.leadingMargin + 127 priority:999>

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x15c5ab880 V:[UIView:0x15c63d860(20)]>",
"<NSLayoutConstraint:0x15c5abb60 V:[UIView:0x15c63d860(10)]>"

当然这些都是不用 IB 和 View Debugging / Reveal 情况下的选择。

还有个优点就是类 ASCII-art,可视化的样式描述。

The Visual Format Language lets you use ASCII-art like strings to define your constraints. This provides a visually descriptive representation of the constraints.

下面是一个 CustomToolBar 的 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import UIKit

/**
Swift NSDictionaryOfVariableBindings substitute

- parameter arr: UIView array: (view1, view2)

- returns: return ["v1": UIView, "v2": view2]
*/
func dictionaryOfNames(arr: UIView ...) -> [String: UIView] {
var d = [String: UIView]()
for (ix, v) in arr.enumerate() {
d["v\(ix+1)"] = v
}
return d
}

class CustomToolBar: UIView {

var textField: UITextField!
var commentCountButton: UIButton!
var commentImageButton: UIButton!

override init(frame: CGRect) {
super.init(frame: frame)

self.backgroundColor = UIColor.whiteColor()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func drawRect(rect: CGRect) {

let topLine = UIView()
topLine.backgroundColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0)
self.addSubview(topLine)

textField = UITextField()
textField.placeholder = "Write some in your deep mind."
self.addSubview(textField)

commentImageButton = UIButton(type: .Custom)
commentImageButton.setBackgroundImage(UIImage(named: "comment"), forState: .Normal)
self.addSubview(commentImageButton)

commentCountButton = UIButton(type: .Custom)
commentCountButton.titleLabel?.font = UIFont.systemFontOfSize(14)
commentCountButton.setTitle("8888888888", forState: .Normal)
commentCountButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
self.addSubview(commentCountButton)

topLine.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
commentImageButton.translatesAutoresizingMaskIntoConstraints = false
commentCountButton.translatesAutoresizingMaskIntoConstraints = false

// NSLayoutConstraintsHelper.swift
let d = dictionaryOfNames(topLine, textField, commentImageButton, commentCountButton)

self.addConstraints([
NSLayoutConstraint.constraintsWithVisualFormat("H:|[v1]|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:|[v1(0.5)]", options: [], metrics: nil, views: d),

NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[v2]-[v3(14)][v4]-|", options: .AlignAllCenterY, metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:|-[v2]-|", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:[v3(14)]", options: [], metrics: nil, views: d),
NSLayoutConstraint.constraintsWithVisualFormat("V:[v4(14)]", options: [], metrics: nil, views: d),
].flatten().map { $0 })
}
}


总结

经过几次项目的实践,发现还是 Visual Format 最好用,简洁直观。

参考

在 Swift 大行其道,Objective-C 渐行渐远的今天,这本书看起来还是获益匪浅。按照52条方法目录摘录一些笔记。

1. 了解 Objective-C 语言的起源

Objective-C 为 C 语言添加了面向对象的特性,是其超集。Objective-C 使用动态绑定的消息结构,运行时才会检查对象的类型。接受一条消息后,究竟执行何种代码,由运行期环境而非编译器决定。

5. 用枚举表示状态、选项、状态吗

6. 理解 “属性”可以概念

自动合成属性:@property,指定实例变量名称:@synthesize,属性特质:原子性/读写权限/读写方法名/内存管理,都做有详细的解释,就不在此赘述了。

7. 在对象内部尽量直接访问实例变量

直接访问实例变量:

  • 不经过方法派发(method dispatch)
  • 不调用设置方法(access methods)
  • 不触发键值观测(KVO)
  • 惰性初始化(lazy initialization)无法完成

11. 理解 objc_msgSend 的作用

例如:

1
id returnValue = [someObject messageName: parameter];

someObject 叫做接受者(receiver),messageName 叫做选择子(selector),选择子和参数合起来称为消息(message)。编译器看到消息后将其转换为一条标准的 C 语言函数调用,所调用函数 objc_msgSend,原型如下:

1
void objc_msgSend(id self, SEL cmd, ...)

编译器会把消息转换为如下函数:

1
2
3
id returnValue = objc_msgSend(someObject, 
@selector(messageName:),
parameter);

objc_msgSend 函数会依据接受者与选择子的类型,来搜寻接受者所属的类中方法列表(list of methods ),找到就跳至实现代码。若找不到就沿着继承体系向上查找。最终找不到就执行消息转发(message forwarding)。

未完

几乎都是两个人的对话,从双方羞涩的看向对方,到因为一对德国夫妻吵架而对视。男主鼓起勇气就此打开话题,聊天聊地,就此很愉快的旅程开始了。不管现实生活和情感是否顺利,但是两人总有新的话题和趣闻。当 Jesse 邀请下车游玩时候故事才真正开始。

开始的找有趣的地方,到后来随意的逛街道,酒吧,看手相,看日落,餐厅里互打电话,晚上草地聊天,次日火车道别。两人不太想落入俗套的留电话地址的偶遇,固执的开始时声明不留电话。因此格外珍惜相遇的这一天,玩的尽兴,聊得深入心灵。酒吧里算是爱情观的吐槽:人总是寻求被爱的多一点。也都是害怕重新承受之前恋爱分手之痛。看手相部分引起了一些争执,Jesse 理性的戳穿手相师和填词诗人的把戏,Céline 的满足和感动都搅合了。餐厅里的打电话很有意思,感觉 Céline 的很多时候也是主动地吐露情愫。

最后火车送别明明很不舍,但双方还是很坚持之前的想法不把这次邂逅变成俗套的艳遇。不过最后也做了妥协,约定六个月后此地在相见,还因为从今天还是昨天算起而争执了一番。说明内心是多么的喜欢对方,却又害怕伤害的顾忌而妥协。

电影里面男女主角都是普通的人的性格特点和际遇,但因为相互吸引和聚到一起,一天虽短,但却异常有趣。或者这样的爱情很奢侈,但是或者这就是不讲婚姻时纯粹的爱情。

Auto Layout Guide: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/index.html#//apple_ref/doc/uid/TP40010853-CH7-SW1

  • 优先使用 stack view,可简化设置(个人认为还是有时候还是非常复杂的布局还是不用,增加复杂度)
  • Constraint等式表示相等,而非赋值,表示两个 view 的关系
  • From the view’s intrinsic content size, it’s easy to understand: Compression Resistance & Content Hugging.
  • 尽量和最邻居的 view 建立 Constraint

参考文章 iOS开发 - 处理不等高TableViewCell的小花招,实践出真知,也是费了一番力气才把文章 Cell 使用 Auto Layout自动算高。眼高手低总是有的,所以以后不仅要学习开发的思路方法论,重要是在实践一遍。

Auto Layout设置好高度约束。不要实现- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

1
2
3
4
5
6
(void)viewDidLoad {
[super viewDidLoad];

self.tableView.estimatedRowHeight = 100; // 随便设个不那么离谱的值
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (TableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"
forIndexPath:indexPath];
cell.avatar.image = [UIImage imageNamed:@"avatar"];
cell.name.text = [NSString stringWithFormat:@"%ld", indexPath.row % 2];
cell.time.text = [NSString stringWithFormat:@"%@", [NSDate date]];
cell.source.text = @"Weibo.com";

NSMutableString *str = [NSMutableString new];
for (long i = 1; i < indexPath.row; i++) {
[str appendString:@"The 1989 World Tour (Live)"];
}
[str appendString:@"END!!!"];
cell.text.text = str;

//移除多余边框,如果是图片是 Aspect Fill 或者 Aspect Fit
cell.pic.layer.borderColor = [UIColor clearColor].CGColor;
cell.pic.layer.borderWidth = 0.1;
cell.pic.layer.masksToBounds = YES;

if (indexPath.row % 2 == 1) {
cell.pic.image = [UIImage imageNamed:@"pic"];
} else {
cell.pic.image = nil;
}

return cell;
}

源码:https://github.com/gewill/test-projects/tree/master/test%20auto%20height%20cell

Swift中的内存指针讲解的很清楚:http://onevcat.com/2015/01/swift-pointer/
Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象。Swift中,指针都使用一个特殊的类型来表示,那就是UnsafePointer。总的来说还是为了方便处理 C API,平时并不需要深入了解。

还有苹果官方博客:Interacting with C Pointers

下面代码是数组指针用法:

    // Swift Memory UnsafePointer
    var array = [1, 2, 3, 4, 5]
    var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count)
    // baseAddress 是第一个元素的指针
    var basePtr = arrayPtr.baseAddress as UnsafeMutablePointer<Int>
    print(basePtr.memory) // 1
    basePtr.memory = 10
    print(basePtr.memory) // 10
    //下一个元素
    var nextPtr = basePtr.successor()
    print(nextPtr.memory) // 2
    print(array) // [10, 2, 3, 4, 5]
    

源于微信群里一个问题讨论,具体陈铭嘉博客总结的很详细:http://www.jianshu.com/p/9a2952c792e6

我主要是实践验证一下文章中的代码,其中Swift中的内存指针参看:http://onevcat.com/2015/01/swift-pointer/

Objective-C 部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//第一层理解:
//如果对一个不可变容器复制,copy是指针复制,即浅拷贝。
//如果对一个可变容器复制,copy是对象复制,即深拷贝。
NSArray *array = [NSArray array];
NSArray *array2 = [array copy];
NSLog(@"%p and %p", array, array2);

NSMutableArray *marray = [NSMutableArray array];
NSMutableArray *marray2 = [marray copy];
NSLog(@"%p and %p", marray, marray2);

// 第二层理解:
// 如果是对可变容器copy,是对象复制,即深拷贝,但拷贝出的是一个不可变容器。
// 如果是对可变容器mutableCopy才符合正确地copy语义,也是对象复制,即深拷贝,这次拷贝出的是一个可变容器。
NSMutableArray *array3 = [NSMutableArray array];
NSLog(@"%@", [array3 class]);
[array3 addObject:@"Panda"];

NSMutableArray *array4 = [array3 mutableCopy];
NSLog(@"%@", [array4 class]);
[array4 addObject:@"Lion"]; //成功

NSMutableArray *array5 = [array3 copy];
NSLog(@"%@", [array5 class]);
//[array5 addObject:@"Lion"]; //报错

// 第三层理解:
// 上述的深拷贝其实还不是完全深拷贝,因为第二层的图可以发现mutableCopy的数组仍然共享同样的数组元素。
// 而完全深拷贝即是对数组元素同样的拷贝的真正深拷贝。
NSMutableArray *marray3 = [NSMutableArray array];
[marray3 addObject:@"Panda"];
NSMutableArray *marray4 = [marray3 mutableCopy]; //一般深拷贝
[marray4 addObject:@"Li"];
NSMutableArray *marray5 = [NSKeyedUnarchiver
unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:marray3]]; //完全深拷贝
NSLog(@"数组第一个元素的指针 -> 1:%p \n 2:%p \n 3:%p", marray3[0], marray4[0], marray5[0]);
NSLog(@"数组的指针 -> 1:%p \n 2:%p \n 3:%p", marray3, marray4, marray5);

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 0x7fd2e2501ff0 and 0x7fd2e2501ff0
0x7fd2e2500710 and 0x7fd2e2501ff0


__NSArrayM
__NSArrayM
__NSArrayI


数组第一个元素的指针 -> 1:0x10c0730a0
2:0x10c0730a0
3:0xa000061646e61505
数组的指针 -> 1:0x7fd2e260f7a0
2:0x7fd2e262c230
3:0x7fd2e260b6b0

Swift 部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//copy array swift
var array1 = [1, 2, 3, 4, 5]
var array4 = array1
var arrayPtr1 = UnsafeMutableBufferPointer<Int>(start: &array1, count: array1.count)
var arrayPtr4 = UnsafeMutableBufferPointer<Int>(start: &array4, count: array4.count)
print(array1)
print(array4)
print(arrayPtr1)
print(arrayPtr4)


array1[0] = 10
array1.append(20)
print(array1)
print(array4)
var arrayPtr7 = UnsafeMutableBufferPointer<Int>(start: &array1, count: array1.count)
var arrayPtr8 = UnsafeMutableBufferPointer<Int>(start: &array4, count: array4.count)
print(arrayPtr7)
print(arrayPtr8)

输出:

1
2
3
4
5
6
7
8
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
UnsafeMutableBufferPointer(start: 0x00007fb568da65f0, length: 5)
UnsafeMutableBufferPointer(start: 0x00007fb568da65a0, length: 5)
[10, 2, 3, 4, 5, 20]
[1, 2, 3, 4, 5]
UnsafeMutableBufferPointer(start: 0x00007fb568da65f0, length: 6)
UnsafeMutableBufferPointer(start: 0x00007fb568da65a0, length: 5)

原文地址:https://swift.org/documentation/api-design-guidelines.html

These are draft API guidelines being developed as part of the Swift 3.0 effort. 这些 API 指南草稿将作为 Swift 3.0的成果的一部分。

1. Fundamentals 最基本的

1.1.

Clarity at the point of use is your most important goal. Code is read far more than it is written. 用法的清晰是最重要的目标。阅读代码远远比写重要。

1.2.

Clarity is more important than brevity. Although Swift code can be compact, it is anon-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate. 清晰比简洁更重要。尽管 Swift 代码可以很简洁,但是目标却不是以最可能少的单词完成尽少量的代码。Swift 代码中,简洁它是存在的,但也仅仅是强类型系统和特性自然而然产生的意外效果。

1.3.

Write a Documentation Comment for every method or property in Swift’s dialect of Markdown. Ideally, the API’s meaning can be understood from its signature and its one- or two-sentence summary: 为每个方法和属性书写注释,并符合Swift 版的Markdown语法,通过签名和一两句概要即可理解 API 的含义。

1
2
3
4
5
6
/// Returns the first index where `element` appears in `self`, 
/// or `nil` if `element` is not found.
///
/// - Complexity: O(`self.count`).
public func indexOf(element: Generator.Element) -> Index? {

Insights gained by writing documentation can have such a profound impact on your API’s design that it’s a good idea to do it early on. 通过写文档获得的洞察力,可以对你设计 API 产生巨大的影响,因此最好今早的开始做。

If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API. 如果你尝试用简单的术语来描述的你的 API 的功能时都有问题,可能你设计的错误的 API。

2. Naming 命名

2.1. Promote Clear Usage 提高清晰的使用方法

2.1.1. Include all the words needed to avoid ambiguity for a person reading code where the name is used. 包含所有必需的单词以避免歧义,当读者看到名字时。

For example, consider a method that removes the element at a given position within a collection 例如,考虑一个方法用来删除集合类的指定位置的元素

1
public mutating func removeAt(position: Index) -> Element

used as follows: 用法如下

1
empliyees.removeAt(x)

If we were to omit the word At from the method name, it could imply to the reader that the method searches for and removes an element equal to x, rather than using x to indicate the position of the element to remove. 如果省略方法名中的单词At,对于读者将意味着这个方法可以搜索并移除一个等于x的元素,而非表示使用x定位将要移除的元素的位置。

1
employees.remove(x) // unclear: are we removing x?

2.1.2. Omit Needless Words. Every word in a name should convey salient information at the use site. 删除多余的单词。名字中每一个单词都表达出使用时的主要信息。

More words may be needed to clarify intent or disambiguate meaning, but those that are redundant with information the reader already possesses should be omitted. In particular, omit words that merely repeat type information: 可能需要更多单词来明确意图或消除歧义,但是多余的信息读者可能会忽略。尤其要删除那些仅仅指明类型信息的单词。

1
public mutating func removeElement(member: Element) -> Element?  allViews.removeElement(cancelButton) 

In this case, the word Element adds nothing salient at the call site. This API would be better: 本例中,Element 这个单词在调用时并没有显著的作用。下面这个 API 可能更好。

1
public mutating func remove(member: Element) -> Element?  allViews.remove(cancelButton) // clearer 

Occasionally, repeating type information is necessary to avoid ambiguity, but in general it is better to use a word that describes a parameter’s role rather than its type. See the next item for details. 个别情况下,重复类型信息室必要的,以避免歧义。但通常来说,使用表示参数的作用单词要比类型信息的单词的更好。下一条项目将会详细讲解。

2.1.3. Compensate For Weak Type Information as needed to clarify a parameter’s role. 对弱类型的补充说明参数的作用描述信息。

Especially when a parameter type is NSObjectAnyAnyObject, or a fundamental type such Int or String, type information and context at the point of use may not fully convey intent. 尤其是当参数类型是NSObjectAnyAnyObject 或者最基本的类型如  Int 或 String,这时候类型信息和使用时的上下文可能不能完全表达出意图。

1
2
func addObserver(_ observer: NSObject, forKeyPath path: String) 
grid.addObserver(self, forKeyPath: graphics) // clear

To restore clarity, precede each weakly-typed parameter with a noun describing its role: 回归到清晰,优先添加描述作用的名词来命名弱类型参数

1
2
func addObserver(_ observer: NSObject, forKeyPath path: String) 
grid.addObserver(self, forKeyPath: graphics) // clear

2.2. Be Grammatical 遵循语法规则

2.2.1. Uses of mutating methods should read as imperative verb phrases, e.g.,x.reverse()x.sort()x.append(y). 变异的方法应该使用命令式的动词短语。

2.2.2. Uses of non-mutating methods should read as noun phrases when possible, e.g. x.distanceTo(y)i.successor(). 非变异的方法尽可能的使用名词短语。

Imperative verbs are acceptable when there is no good alternative that reads as a noun phrase: 必要的使用动词也是可接受的,当没有其他好的可以读起来像一个名字短语的选项。

1
let firstAndLast = fullName.split() // acceptable

2.2.3. When a mutating method is described by a verb, name its non-mutating counterpart according to the “ed/ing” rule, e.g. the non-mutating versions ofx.sort() and x.append(y) are x.sorted() and x.appending(y). 与变异之对应的非变异自己的变异方法通常是动词加“ed/ing”

Often, a mutating method will have a non-mutating variant returning the same, or a similar, type as the receiver. 通常,一个非变异自己的变异的方法返回值是一个相同或者相似类型。

Prefer to name the non-mutating variant using the verb’s past tense (usually appending “ed”): 偏爱用动词过去式(通常是结尾添加“ed”)命名一个非变异体

1
2
3
4
5
6
7
8
/// Reverses `self` in-place. 
mutating func reverse()

/// Returns a reversed copy of `self`.
func reversed() -> Self
...
x.reverse()
let y = x.reversed()

When adding “ed” is not grammatical because the verb has a direct object, name the non-mutating variant using the verb’s gerund form (usually appending “ing”): 当因为及物动词添加“ed”不和语法时,就使用动名词形式(通常结尾添加“ing”)来命名

1
2
3
4
5
6
7
8
9
/// Strips all the newlines from \`self\` 
mutating func stripNewlines()

/// Returns a copy of \`self\` with all the newlines stripped.
func strippingNewlines() -> String
...
s.stripNewlines()
let oneLine = t.strippingNewlines()

2.2.4. Uses of non-mutating Boolean methods and properties should read as assertions about the receiver, e.g. x.isEmptyline1.intersects(line2). 非变异的布尔方法和属性应使用断言作为接收器。

2.2.5. Protocols that describe what something is should read as nouns (e.g. Collection). Protocols that describe a capability should be named using the suffixes able,ible, or ing (e.g. EquatableProgressReporting). 描述是什么的协议用名词。描述能力的用 able,ible, or ing 等后缀。

2.2.6. The names of other types, properties, variables, and constants should read as nouns. 其余类型,属性,变量和常量用名词。

2.3. Use Terminology Well 恰当地使用术语

Term of Art

noun - a word or phrase that has a precise, specialized meaning within a particular field or profession. 一个单词或短语,有明确的特定的含义在某一特定领域或专业。

2.3.1. Avoid obscure terms if a more common word conveys meaning just as well. Don’t say “epidermis” if “skin” will serve your purpose. Terms of art are an essential communication tool, but should only be used to capture crucial meaning that would otherwise be lost. 避免使用生僻词。

The only reason to use a technical term rather than a more common word is that it precisely expresses something that would otherwise be ambiguous or unclear. Therefore, an API should use the term strictly in accordance with its accepted meaning.

  • Don’t surprise an expert: anyone already familiar with the term will be surprised and probably angered if we appear to have invented a new meaning for it.

  • Don’t confuse a beginner: anyone trying to learn the term is likely to do a web search and find its traditional meaning.

2.3.2. Stick to the established meaning if you do use a term of art.Avoid abbreviations. Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms. 坚持使用明确的单词。避免缩写,除非很容易搜索到原意。

>The intended meaning for any abbreviation you use should be easily found by a web search. 

2.3.3. Embrace precedent: Don’t optimize terms for the total beginner at the expense of conformance to existing culture. 拥抱先例:不要为了新手,以顺应先前的文化为代价去优化术语。

译者注:#Swift3 Remove the ++ and – operators
Author: Chris Lattner https://github.com/apple/swift-evolution/blob/master/proposals/0004-remove-pre-post-inc-decrement.md

3. Conventions 约定

3.1. General Conventions 一般约定

  • Document the complexity of any computed property that is not O(1). People often assume that property access involves no significant computation, because they have stored properties as a mental model. Be sure to alert them when that assumption may be violated. 指出任何复杂度非O(1)的计算属性。因为人们通常假设属性访问并非复杂计算量。

  • Prefer methods and properties to free functions. Free functions are used only in special cases. 偏爱方法和属性,而非相对独立的函数。

  • Follow case conventions: names of types, protocols and enum cases are UpperCamelCase. Everything else is lowerCamelCase. 遵循驼峰大小写法:类型,协议和枚举用大驼峰法,其余小驼峰法。

  • Methods can share a base name when they share the same basic meaningbut operate on different types, or are in different domains. 方法可以共用一个通用名,如果均指一个基本含义时,用于不同的类型或者不同的作用域。

    For example, the following is encouraged, since the methods do essentially the same things:

    1
    2
    3
    4
    5
    6
    7
    extension Shape { 
    /// Returns true iff other is within the area of self.
    func contains(other: Point) -> Bool { ... }
    /// Returns true iff other is entirely within the area of self.
    func contains(other: Shape) -> Bool { ... }
    /// Returns true iff other is within the area of self.
    func contains(other: LineSegment) -> Bool { ... } }

3.2. Parameters

  • 3.2.1. Take advantage of defaulted arguments when it simplifies common uses. Any parameter with a single commonly-used value is a candidate for defaulting. 充分利用参数的默认值。

  • 3.2.2. Prefer to locate parameters with defaults towards the end of the parameter list. Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked. 尽量含默认值参数置于参数列表后面。

  • 3.2.3. Prefer to follow the language’s defaults for the presence of argument labels 遵循语言习惯填写外部参数标签。

    In other words, usually: 换言之

    • First parameters to methods and functions should not have required argument labels. 第一参数不必指明外部参数。

    • Other parameters to methods and functions should have required argument labels. 后面的外部参数必须填写。

    • All parameters to initializers should have required argument labels. 所有涉及初始化的参数均需外部参数。

      The above corresponds to where the language would require argument labels if each parameter was declared with the form: 如果符合上述需要外部参数标签,应该向下面这么声明
      

swift ​ identifier: Type ​

>译者注:参看 http://stackoverflow.com/questions/24815832/when-are-argument-labels-required-in-swift

There are only a few exceptions: 也有例外

  • In initializers that should be seen as “full-width type conversions,” the initial argument should be the source of the conversion, and should be unlabelled. 包含全角的类型转换的初始化应该使用原始指,而且不包含外部参数标签。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension String { 
    // Convert `x` into its textual representation in the given radix init(_ x: BigInt, radix: Int = 10) // Note the initial separate underscore
    }
    text = "The value is: "
    text += String(veryLargeNumber)
    text += " and in hexadecimal, it's"
    text += String(veryLargeNumber, radix: 16)
    ​ ```

    In “narrowing” type conversions, though, a label that describes the narrowing is recommended:

    ```Swift
    extension UInt32 {
    init(_ value: Int16) // widening, so no label
    init(truncating bits: UInt64)
    init(saturating value: UInt64) }
  • When all parameters are peers that can’t be usefully distinguished, none should be labelled. Well-known examples include min(number1, number2) andzip(sequence1, sequence2). 如果所有参数是平等不易区分重要性,不用加外部参数标签。

  • When the first argument is defaulted, it should have a distinct argument label. 如果第一个参数是值可空,那么就应该添加一个清晰的外表参数标签。

    1
    2
    3
    4
    5
    extension Document { 
    func close(completionHandler completion: ((Bool) -> Void)? = nil)
    }
    doc1.close()
    doc2.close(completionHandler: app.quit)

    As you can see, this practice makes calls read correctly regardless of whether the argument is passed explicitly. If instead you omit the parameter description, the call may incorrectly imply the argument is the direct object of the “sentence:”

    1
    2
    3
    4
    5
    6
    extension Document { 
    func close(completion: ((Bool) -> Void)? = nil)
    }

    doc.close(app.quit) // Closing the quit function?

      If you attach the parameter description to the function’s base name, it will “dangle” when the default is used:
      
    
    1
    2
    3
    4
    extension Document { 
    func closeWithCompletionHandler(completion: ((Bool) -> Void)? = nil)
    }
    doc.closeWithCompletionHandler() // What completion handler?

4. Special Instructions 特别说明

4.1. Take extra care with unconstrained polymorphism (e.g. AnyAnyObject, and unconstrained generic parameters) to avoid ambiguities in overload sets. 特别注意不受约束类型的多态。

For example, consider this overload set:

1
2
3
4
5
6
7
8
9
struct Array { 
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(newElement: Element)

/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func append<
S : SequenceType where S.Generator.Element == Element
>(newElements: S) }

These methods form a semantic family, and the argument types appear at first to be sharply distinct. However, when Element is Any, a single element can have the same type as a sequence of elements:

1
var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? 

To eliminate the ambiguity, name the second overload more explicitly: 为了排除歧义,重新命名时更加明确含义。

1
2
3
4
5
6
7
8
9
struct Array { 
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(newElement: Element)

/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func appendContentsOf<
S : SequenceType where S.Generator.Element == Element
>(newElements: S) }

Notice how the new name better matches the documentation comment. In this case, the act of writing the documentation comment actually brought the issue to the API author’s attention.

4.2. Make documentation comments tool-friendly; they will be automatically extracted to generate richly-formatted public documentation, and they appear in Xcode, in generated interfaces, quick help, and code completion. 文档注释对工具优化,方便地自动提取富文本的文档,自动显示在 Xcode,生成界面,快速帮助,代码补全中。

Our Markdown processor gives special treatment to the following bullet list keywords: Swift 版 Markdown 会特殊处理下面列出的关键词

-Attention: -Important: -Requires:
-Author: -Invariant: -See:
-Authors: -Note: -Since:
-Bug: -Postcondition: -Throws:
-Complexity: -Precondition: -TODO:
-Copyright: -Remark: -Version:
-Date: -Remarks: -Warning:
-Experiment: -Returns:

Writing a great summary is more important than leveraging keywords. 写一个很棒的概要远比仅仅添加关键词重要的多。

You can omit separate documentation for each parameter and the return type if it wouldn’t add useful information beyond what’s already conveyed by the method signature and its summary line. For example: 除方法签名和概要信息外,如果不能添加有用的信息,就可以省略参数和返回类型的注释文档。

1
2
/// Append `newContent` to this stream. 
mutating func write(newContent: String)
0%