源于微信群里一个问题讨论,具体陈铭嘉博客总结的很详细: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)

前言

正如一个朋友说得:“开发这个工作讲究的就是自学能力。” 我最近开发一款 App 希望能够同时阅读 Twitter 和微博,接触到官方文档时候,就想偷懒去开源第三方 SDK,发现太多bug。还不如官方文档解释的清楚和步骤详细。
而且忘记了方法论,就是开发一款 App,罗列出 UI 和技术框架,但是遇到全英文的文档时候也懵了。仔细想想其实也就是 TwitterKit 封装 iOS 一些常见用法的 API,但是总的来说还是基于 oAuth 和 REST ,其中 REST 都很熟悉了,无非就是 oAuth 比较麻烦一般都是每个公司封装不一样,需要安装文档一步一步来配置就好了。

总结下来,遇到新的技术要注意方法:

  • 对比是否接触过类似的技术问题
  • 总结提取大纲分类
  • 不认识单词一定要翻译清楚
  • 实践部分一步一步的做

吐槽一下 dev.twitter.com 的三级菜单真是很隐晦,不是一个好的设计。
dev.twitter.com

Twitter 集成 TwitterKit

下载 Fabric.app,添加 pod,安装提示步骤来即可。

1
2
pod 'Fabric'
pod 'TwitterKit'

访问:https://docs.fabric.io/ios,使用其 Authentication 和 REST API 即可。

The REST API can be used to make authenticated Twitter API requests. Though it can be accessed manually, we recommend using the convenience methods whenever possible.

微博集成 LeanCloudSocial

1
2

pod 'LeanCloudSocial'

文档:https://leancloud.cn/docs/sns.html

LeanCloudSocial是只有一键登录,属于轻量级添加微博/微信/QQ登录支持,没有其他微博相关 REST API 可用。后面微博部分需要手动或者可能换成微博官方 SDK。

The Swift Programming Language 2.1 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
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//: Playground - noun: a place where people can play

import UIKit

//: 方法(Methods)
//: 方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。

//: 实例方法 (Instance Methods)
//: 实例方法是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致.

//: self 属性(The self Property)
//: 类型的每一个实例都有一个隐含属性叫做self,self完全等同于该实例本身。


//: 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
//: 结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}

var somePoint = Point(x: 3.0, y: 3.0)
somePoint.moveByX(2.0, y: 3.0)


//: 在可变方法中给 self 赋值(Assigning to self Within a Mutating Method)
struct Point1 {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point1(x: x + deltaX, y: y + deltaY)
}
}
//: 这里不仅仅学习一个语法,而是思考为什么费劲把默认不可变的枚举变成可变,是因为提供一个可能结合枚举特性,写出简洁表达能力强的代码。
//: Ash Furrow: Ideas vs Syntax. Watch video here: https://youtu.be/YsUTuwpbURA
enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
case Off: self = Low
case Low: self = High
case High: self = Off
}
}
}

var ovenLight = TriStateSwitch.Low
ovenLight.next()
ovenLight.next()
//: 类型方法 (Type Methods)

//: 实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做类型方法。声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。类可能会用关键字class来允许子类重写父类的方法实现。


//: 在 Objective-C 中,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。

class SomeClass {
static func someTypeMethod() {
// Do something
print("This is a type method")
}
}
SomeClass.someTypeMethod()


//: 下面的例子定义了一个名为LevelTracker结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)
//: 也就是定结构体类型方法的用途,可以直接把逻辑加到定义的数据类型中

struct LevelTracker {
static var highestunlockedLevel = 1
static func unlockLevel(level: Int) {
if level > highestunlockedLevel { highestunlockedLevel = level }
}
static func levelIsUnlocked(level: Int) -> Bool {
return level <= highestunlockedLevel
}
var currentLevel = 1
mutating func advanceToLevel(level: Int) -> Bool {
if LevelTracker.levelIsUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}

class Player {
var tracker = LevelTracker()
let playName: String
func compeletedLevel(level: Int) {
LevelTracker.unlockLevel(level + 1)
tracker.advanceToLevel(level + 1)
}
init(name:String) {
playName = name
}
}


var player = Player.init(name: "Will")
player.compeletedLevel(4)
print(LevelTracker.highestunlockedLevel)

player = Player.init(name: "Ge")
if player.tracker.advanceToLevel(6) {
print("Level 6 has been unlocked.")
} else {
print("Level 6 has not yet been unlocked.")
}

The Swift Programming Language 2.1 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
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//: Playground - noun: a place where people can play

import UIKit

//: 属性将值和特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。


//: 存储属性(Stored Properties)
struct FiexdLengthRange {
var firstValue: Int
let length: Int
}

var x = FiexdLengthRange(firstValue: 1, length: 3)
x.firstValue = 4

//: 常量结构体的存储属性均为常量
let y = FiexdLengthRange(firstValue: 4, length: 2)


//: 延迟存储属性
class DataImporter {
var firstName = "data.txt"
}

class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}

let manager = DataManager()
manager.data.append("New")
manager.data.append("Two")


//: 存储属性没有实例变量,属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。


//: 计算属性(Computed Properties): 除存储属性外,类、结构体和枚举可以定义计算属性。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
struct Point {
var x = 0.0, y = 0.0
}

struct Size {
var width = 0.0, height = 0.0
}

struct Rect {
var origin = Point()
var size = Size()
var center: Point {
mutating get {
let centerX = origin.x + size.width / 2
let centerY = origin.y + size.height / 2
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - size.width / 2
origin.y = newCenter.y - size.height / 2
}
}
}


var rect0 = Rect(origin: Point(), size: Size(width: 3.0, height: 4.0))
rect0.center
rect0.center = Point(x: 50, y: 50)
rect0.center

//: 只读计算属性(Read-Only Computed Properties)
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
Cuboid(width: 4.0, height: 5.0, depth: 2.0).volume


//: 属性观察器(Property Observers)
//: 属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。不需要为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。

class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotolSteps) {
if totalSteps != newTotolSteps {
print("The totalSteps will change value to \(totalSteps).")
}
}
didSet {
if totalSteps > oldValue {
print("The totalSteps add \(totalSteps - oldValue) steps. ")
}
}
}
}

StepCounter().totalSteps = -33
var stepCounter = StepCounter()
stepCounter.totalSteps = 33
stepCounter.totalSteps = 11



//: 全局变量和局部变量(Global and Local Variables)
//: 全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy修饰符。局部范围的常量或变量从不延迟计算。


//: 类型属性(Type Properties)
//: 使用关键字static/class来定义类型属性,必须指定默认值,延迟初始化的。类型属性是通过类型本身来访问

struct SomeStructure {
static var storedTypedProperty = "some value"
static var computedTypedProperty: Int {
return 1
}
}

enum SomeEnumeration {
static var storedTypedProperty = "some value"
static var computedTypedProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypedProperty = "some value"
static var computedTypedProperty: Int {
return 7
}

class var overrideableComputedTypeProperty: Int {
return 109
}
}


struct AudioChannel {
static let threshoudLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.threshoudLevel {
currentLevel = AudioChannel.threshoudLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)


Watch video here: https://youtu.be/YsUTuwpbURA

It’s much clear to understand what’s the Ash talk about by list the outlines

1. Agenda

  • We’ve been here before
  • Learning is forever, deal with it
  • Never throw ideas away
  • How to force yourself to think
  • Always be abstracting

2. Ideas vs Syntax

Object Literals/Blocks & GCD/Swift 2: Guard/Currying/Enums

New syntax lets us do new things
However! Syntax is only a tool
Like blocks, Swift 2 syntax is most useful when it enables new ideas

That’s all just syntax. What matters are ideas.

3. Benefits of Testing

  • Limited object scope is good
    • High cohesion, low coupling
  • How to limit scope?
    • Controlling public interface and dependencies

Things to never throw away:Code & Ideas

Changing a unit test?

  • No -> Refactoring
  • Yes -> Rewriting

4. Dependency Injection

Your things shouldn’t create the things they need

1
class ViewController: UIViewController {
let networkController = NetworkController()
    func viewDidLoad() {
        super.viewDidLoad()
        networkController.fetchStuff {
            self.showStuff()
        }
} }
1
class ViewController: UIViewController {
var networkController: NetworkController?
    func viewDidLoad() {
        super.viewDidLoad()
        networkController?.fetchStuff {
            self.showStuff()
        }
} }

5. Unit Testing

  • Don’t test private functions
    • Also, start marking functions as private
  • Remember, we want to avoid rewriting
  • Don’t test the implementation
  • Don’t use “partial mocks”
    • See @searls post on partial mocks

6. Wrap Up

  • We have a history of being awesome, let’s keep it up
  • Learning isn’t just for when Xcode is in beta
  • Ideas are more valuable than code, but throwing away either is dangerous
  • Effective unit tests make it easy to change code
  • Operate at the highest level of abstraction you can at any given time

1. 前言

以前只知道怎么用 Delegate,能够传值和回传值。最近写代码封装一个 UIPickerView+UIButton,时候需要传值,就想到了 Delegate。但是自己写一个却把我难住了。最后看了还是看了一个 Demo, 单步调试才算真正理解的Delegate 整个流程和原理。

简单来说就是声明 Protocol,添加触发 调用Protocol 方法的条件,被委托者设置 Delegate = self,就会在触发条件下调用 Protocol 方法(参数和返回值可实现双向传值)。

主要看了这个教程:
CREATE CUSTOM DELEGATE AND PROTOCOL IOS | SWIFT & OBJECTIVE-C,下面是作者提供的源码Objective-C  ProjectSwift Project

教材中MyTimer是Custom ViewController,我实际工作中是 UIView,也是参考了另外一篇 Apple 的教程:Implement a Custom Control。发现开发真的离不开 Google,但是前提是要有思路,或者对一个项目或者难题,要有整体的框架和可用的知识点熟悉。

2. 源码

下面是UIPickerView+UIButton封装后的源码:

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
//  GenderPickerView.swift

import UIKit

protocol GenderPickerViewDelegate: NSObjectProtocol {
func genderPickerDidSelectItem(row: Int)
}

class GenderPickerView: UIView, UIPickerViewDataSource, UIPickerViewDelegate {
var delegate: GenderPickerViewDelegate!
var toolbar: UIToolbar!
var picker: UIPickerView!
var genderPickerViewData = ["不告诉你", "男", "女"]
var genderSelectedRow = 0

override init(frame: CGRect) {
super.init(frame: frame)
toolbar = UIToolbar.init(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, 20))
toolbar.barStyle = .Default
toolbar.sizeToFit()

let cancelButton = UIBarButtonItem.init(title: "取消", style: UIBarButtonItemStyle.Done, target: self, action: "genderPickerDidCancel")
let flexibleButton = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil)
let doneButton = UIBarButtonItem.init(title: "确定", style: UIBarButtonItemStyle.Done, target: self, action: "genderPickerDidSelect")
toolbar.setItems([cancelButton, flexibleButton, doneButton], animated: true)

picker = UIPickerView.init(frame: CGRectMake(0, 44, UIScreen.mainScreen().bounds.width, 120))
picker.dataSource = self
picker.delegate = self

addSubview(toolbar)
addSubview(picker)
}

convenience init() {
self.init(frame: CGRectMake(0, UIScreen.mainScreen().bounds.height - 164, UIScreen.mainScreen().bounds.width, 164))
}

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


//MARK: - 性别选择视图
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 3
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return genderPickerViewData[row]
}

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
genderSelectedRow = row
}

func genderPickerDidSelect() {
delegate?.genderPickerDidSelectItem(genderSelectedRow)
removeFromSuperview()
}

func genderPickerDidCancel() {
removeFromSuperview()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//  PersonalInfoViewController.swift

import UIKit

class ViewController: UIViewController, GenderPickerViewDelegate {
var genderPicker: GenderPickerView!
override func viewDidLoad() {
super.viewDidLoad()
genderPicker = GenderPickerView.init()
genderPicker.delegate = self
self.view.addSubview(genderPicker)}

//MARK: - GenderPickerViewDelegate
func genderPickerDidSelectItem(row: Int) {
// Do something
}
}

Git开始使用的是 GitHub 的 GUI,基本上就是Commit和Sync,涉及项目协作是 Pull Request,基本没问题,但是整个Git的流程和原理还是不懂。

最近公司项目是命令行,涉及合并冲突工作流等等,还是命令行来的清晰直接,又仔细看了Pro Git Book,这里梳理一下Git的工作原理和常用的命令。

给他的工作流程如下:

  • 多个分支:checkout branch
  • 上传修改:add/commit/push
  • 避免冲突:Xcode 每次只提交个人修改的文件,之后可以舍弃多余修改,尤其是 xib 文件,只要打开就会被修改
  • 合并分支:merge

其中2个概念要理清楚,一个是分支用来分版本分人员的作用,另一个是本地工作区和远程仓库。工作原理和概念理解了,常用的几个命令实践几次也就记住了,太多不常用甚至没用命令,需要时可在 Google。

The Swift Programming Language 2.1 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
42
43
44
45
46
47
48
49
50
51
52
53
54
//: Playground - noun: a place where people can play

import UIKit

//: 类和结构体对比
struct Resolution {
var width = 0
var height = 0
}

class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}

//: 类和结构体实例
//: 一个引用类型一个值类型 Classes are reference types, Structures and Enumerations are value types

let someResolution = Resolution()
let someVideoMode = VideoMode()

someResolution.height
someVideoMode.frameRate
someVideoMode.resolution.height

let vga = Resolution(width: 640, height: 480)
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.height = 3096
cinema.width
hd
let tenEighty = VideoMode()
var asloTenEighty = tenEighty
asloTenEighty.frameRate = 30.0
tenEighty.frameRate


//: 恒等运算符 Identity Operators : === !==
if tenEighty === asloTenEighty {
print("These two variables refer to the same instance")
}

//: 指针 Pointers
//: Swift指针类型不用带 *

//: 类和结构体的选择: 结构体适合存储简单数据值和拷贝行为, 不需要继承另一个类型属性或者行为

//: String/Array/Dictionary 类型的赋值和复制行为:Assignment and copy behaviors on Strings, Arrays and Dicitonaries

//: Swift 均以结构体形式实现,值拷贝,但是处于性能优化,只哟确有必要时才会实际执行拷贝;Objective-C是类,传递的是实例的引用。


最近学会了使用 Surge + Shadowsocks,终于可以优雅地在 iOS上分流模式上网了。

购买的是 host1plus.com的VPS,$2一个月好便宜的说。只有一个SSH ,一切都是命令行搞定。折腾了几天后发现,命令行完全是一个简洁高效操作计算机的方式。相比GUI的程序,唯一的缺点:可操作项和文件目录等不能够一目了然的看到,最多一个--help,还是不行就要求助Google了。当然熟悉了命令行之后这也不是问题,就是记忆多一些嘛。

首先安装了Shadowsocks,后来有安装了 WordPress 256的内存瞬间就满了。使用speedtest-cli测试下来, 主机的速度1000M/100M,但是连接上海服务器就只有20M/2M左右。手机也算够用了,毕竟电脑上有强大的Lantern。

最坑的是一般搜索的官方教程都是有坑的,一般都要找一些个人安装文章才能完美运行。PHP和FTP都是找了好多教程才搞定的,下次在配置一定官方+个人教程,搜索的时候也注意筛选最近一周或一月的结果比较靠谱。

0%