看了下自己的文章记录,距离上次文章已经快两个月,距第一篇文章已经有快5个月了。本以为自己有了写技术文章的这个好习惯,没想到还是因为某些因素没能坚持,比如带项目 =。= ...
本来的开发组长当的好好的,抓壮丁让我去带项目,还没产品经理。我一个人又要出原型,想交互,还要管理团队,真的是mmp哟。幸亏UI说你直接草图给我就好,幸亏Axure简单功能不难,幸亏storyboard和设计工具一样都能出图。要不是想着带项目能让我成长,我才不干咧!
前言说完了,因为项目需要,面试了好多个iOS,发现自身有些不足,并且非科班出身所以基础知识也(ji)不(hu)牢(mei)固(you)。然后网上找了下swift进阶的书,看到了《Swift进阶》这本书,喵神翻译的,而且今年5月才发售,价值连城啊,立马上某东买去。顺便下载了一个《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》.pdf
项目二期结束了,我决定开始我的Swift进阶之旅。顺便做个记录,监督自己
每次的记录我都会写一些书里的重点内容和相关的文章。
2017.07.31
结构体和枚举是值类型,引用是一种特殊类型的值,==有时候被称为结构相等,而===则被称为指针相等或者引用相等。
Swift3.0 - 类和结构体的区别
Swift 中的枚举,你应该了解的东西
2017.08.01
什么? 朕的大清亡了???
公司解散你敢信?其他部门遭遇严重生产事故,公司整体解散,只留骨干整体进入其他技术公司。我的天,第一次遭遇公司解散,简直就是
无心撸代码,玩炉石去了。
2017.08.02
生活还是要继续啊,很开心我又开始做开发了,项目还是要继续的。只不过不用我带,也不用我想需求了(这一点我还是很开心的)。虽然带项目才两个月的时间,虽然这种短暂的经历不一定会对技能树有(mei)影(bian)响(hua),但是也算一个经历咯。
引用是一种特殊类型的值:它是一个“指向”另一个值的值。对于一个类的实例,我们只能在变量里持有对它的引用,然后使用这个引用来访问它。
class Person {
var name:String!
init(name:String){
self.name = name
}
}
let person1 = Person.init(name: "Bart")
person1.name
let person2 = Person.init(name: "Lisa")
person2.name
代码里person1和person2指向同一个地址,而.name的内容却是不一样的。
两个引用可能会指向同一个值,这引入了一种可能性,那就是这个值可能会被程序的两个不同的部分所改变。
Swift 风格指南
https://swift.org/documentation/api-design-guidelines/
对于代码编写原则,我将简单明了,并且常用的原则整理出来。
- 务必为函数添加文档注释——特别是泛型函数。
- 类型使用大写字母开头,函数,变量和枚举成员使用小写字母开头,两者都使用驼峰式命名法。
- 优先选择结构体,只在确实需要使用到类特有的特性或者是引用语义时才使用类。
- 除非你的设计就是希望某个类被继承使用,否则都应该将它们标记为final。
- 使用guard来提早退出的方法。
- 不要写重复的代码。如果你发现你写了好几次类似的代码片段,那么试着将它们提取到一个函数里,并且考虑将这个函数转化为协议扩展的可能性。
内建集合类型
数组
数组是一系列相同类型的元素的有序的容器
var x = [1,2,3]
var y = x
y.append(4)
x
//x不会发生改变
//不想让b发生改变,但是任然被a影响
let a = NSMutableArray.init(array: [1, 2, 3])
let b:NSArray = a
a.insert(4, at: 3) //[1, 2, 3, 4]
b //[1, 2, 3, 4]
//不想让d发生改变, 正确方法
let c = NSMutableArray.init(array: [1, 2, 3])
let d = c.copy() as! NSArray
c.insert(4, at: 3) //[1, 2, 3, 4]
d //[1, 2, 3]
Swift标准库中的所有集合类型都使用了“写时复制”这一技术,它能够保证只在必要的时候对数据进行复制。在我们的例子中,直到y.append被调用之前,x和y都将共享内部的存储。
2017.08.03
数组变形
一些数组的方法
mutabFibs //[0, 1, 1, 2, 3, 5, 8, 13, 21]
var new = [Int]()
//去掉数组首个元素
for value in mutabFibs.dropFirst(){
new.append(value)
}
new //[1, 1, 2, 3, 5, 8, 13, 21]
var new1 = [Int]()
//去掉数组后6个元素
for value in mutabFibs.dropLast(6){
new1.append(value)
}
new1 //[0, 1, 1]
var new2 = [Int]()
// 如果越界new2会是空数组
for value in mutabFibs.dropLast(10){
new2.append(value)
}
new2
//遍历元素和对应的下标
for (index, value) in mutabFibs.enumerated(){
print("index:\(index) value:\(value)")
}
数组的map方法
let math = [2,4,6,8]
var squared:[Int] = []
//把数组中的数字平方
for value in math{
squared.append(value * value)
}
squared //[4, 16, 36, 64]
let squared1 = math.map({num in num * num}) //不知道为什么会循环5次
squared1 //[4, 16, 36, 64]
//new.map({})
extension Array{
func map<T>(_ transform: (Element) -> T) -> [T] {
var result: [T] = []
result.reserveCapacity(count)
for x in self{
result.append(transform(x))
}
return result
}
}
//删选出数组里大于10的元素
let filter = mutabFibs.filter { $0 > 10 }
filter //[13, 21]
//删选出数组里大于3的元素
let test = math.filter { (tt) -> Bool in
return tt > 3
}
test //[4, 6, 8]
上面关于Array的扩展是map函数可能的一种实现方式
Element是数组中包含的元素类型占位符,T是元素转换之后的类型的占位符。
关于.filter,我的理解就是对于你的需要,返回true就保留这个元素,返回false就弃掉这个元素
let names = ["Paula", "Elena", "Zoe"]
var lastNameEndingInA: String?
//反向遍历出名字中带有a的最早的元素(找出就终止循环)
for name in names.reversed() where name.hasSuffix("a") {
lastNameEndingInA = name
break
}
lastNameEndingInA // Optional("Elena")
//反向遍历 .reversed()
for na in names.reversed(){
if na.hasSuffix("a"){
print(na) //Elena
//Paula
}
}
extension Sequence{
func last(where predicate: (Iterator.Element) -> Bool) -> Iterator.Element? {
for element in reversed() where predicate(element){
return element
}
return nil
}
}
let match = names.last(where: { $0.hasSuffix("a") })
match
哎,今天就到这儿了。大神的高阶函数写法是真的高,小弟受教了啊!
2017.08.04
可变和带有状态的闭包
extension Array {
func accumulate<Result>(_ initialResult: Result,_ nextPartial:(Result, Element) -> Result) -> [Result]{
var running = initialResult
return map({ (next) -> Result in
print("before:\(running)")
print("next:\(next)")
running = nextPartial(running, next)
print("after running:\(running)")
return running
})
}
}
var numArray = [5,16,23,44]
let acc = numArray.accumulate(3, +)
acc //[8, 24, 47, 91]
let error = numArray.accumulate(3) { (count, num) -> Int in
return count * num
}
error //[15, 240, 5520, 242880]
var char = ["boy", "girl", "children"]
let ttt = char.accumulate("bad", +)
ttt //["badboy", "badboygirl", "badboygirlchildren"]
let errorStr = char.accumulate("god") { (first, str) -> String in
return "\(first) \(str)"
}
errorStr //["god boy", "god boy girl", "god boy girl children"]
extension String{
func addtest<T>(objc:(String, Int) -> T) -> T{
let tt = objc(self, 4)
return tt
}
}
var four = "four is "
let ff = four.addtest { (str, num) -> String in
return str + "\(num)"
}
ff
我昨天就开始试图读懂这个了,就是不明白!!尤其是我回车让方法自动展开,完全不知道方法nextPartial:(Result, Element) -> Result
是怎么可以放+号的。running = nextPartial(running, next)
更不用说这里为什么会自动根据传过来的符号就开始运算了。let acc = numArray.accumulate(3, +)
这个加号看得我这种初级程序员一脸懵逼,传递(Result,Element)
类型并得到Result
类型的返回值的函数是怎么简写成+就结束了,+号怎么传递过去让程序知道你想进行相加操作的呢? 我扩展开是需要自己在返回值的时候去定义加减乘除让程序知道的。
哎,最后拓展了个方法试图让自己明白这个+号,发现还是不知道如何简写成那样。望看到的大指点指点我。
filter会创建一个全新的数组
var filterArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let fl = filterArray.filter({ $0 % 2 == 0})
fl //[2, 4, 6, 8, 10]
let f2 = filterArray.filter { (num) -> Bool in
return num % 2 == 0
}
f2 //[2, 4, 6, 8, 10]
let sq = (1..<10).map { $0 * $0 }.filter { (num) -> Bool in
return num % 6 == 0
}
sq //[2, 4, 6, 8, 10]
extension Array {
func filter(_ isIncluded: (Element) -> Bool) -> [Element] {
var result: [Element] = []
for x in self where isIncluded(x) {
print("==\(x)==")
print(isIncluded(x))
result.append(x)
}
return result
}
}
let fibs = [1, 2, 3, 4, 5]
//Reduc
var total = 0
for num in fibs {
total = total + num
}
total //15
let sum = fibs.reduce(0, {total, num in total + num})
sum //15
//我现在理解是消消乐,相同的参数抵消,留下了运算符
let sum1 = fibs.reduce(0, +)
sum1 //15
let sum2 = fibs.reduce(0) { (total, num) -> Int in
return total + num
}
sum2 //15
//reduc实现
extension Array {
func reduce<Result>(_ initialResult: Result,
_ nextPartialResult: (Result, Element) -> Result) -> Result
{
var result = initialResult
for x in self {
result = nextPartialResult(result, x)
}
return result
}
}
后面看到flatMap举得例子有点绕,待我看懂分析好了再与诸君分享。
看书真的是枯燥啊,共勉之!
2017.08.05
flatMap
有时候我们会想要对一个数组用一个函数进行map,但是这个变形函数返回的是另一个数组,而不是单独的元素。
//内部实现
extension Array {
func flatMap<T>(_ transform: (Element) -> [T]) -> [T] {
var result: [T] = []
for x in self {
print("objc:\(x)")
result.append(contentsOf: transform(x))
print("result:\(result)")
}
return result
}
}
let suits = ["♠", "❤", "♣", "♦"]
let ranks = ["J","Q","K","A"]
let result = suits.flatMap { suit in
ranks.map { rank in
(suit, rank)
}
}
result //[("♠", "J"), ("♠", "Q"), ("♠", "K"), ("♠", "A"), ("❤", "J"), ("❤", "Q"), ("❤", "K"), ("❤", "A"), ("♣", "J"), ("♣", "Q"), ("♣", "K"), ("♣", "A"), ("♦", "J"), ("♦", "Q"), ("♦", "K"), ("♦", "A")]
//flatMap示例
let test = ranks.flatMap { (str) -> [String] in
return ["test", str]
}
test //["test", "J", "test", "Q", "test", "K", "test", "A"]
//拆解出ranks.map
let test1 = ranks.map { (rank) -> (String, String) in
return ("test", rank)
}
test1 //[(.0 "test", .1 "J"), (.0 "test", .1 "Q"), (.0 "test", .1 "K"), (.0 "test", .1 "A")]
//组合, 省调返回值声明和return可以由系统自动判断,简化了很多。
let result2 = suits.flatMap { (hua) -> [(String, String)] in
return ranks.map({ (char) -> (String, String) in
return (hua, char)
})
}
result2 //[("♠", "J"), ("♠", "Q"), ("♠", "K"), ("♠", "A"), ("❤", "J"), ("❤", "Q"), ("❤", "K"), ("❤", "A"), ("♣", "J"), ("♣", "Q"), ("♣", "K"), ("♣", "A"), ("♦", "J"), ("♦", "Q"), ("♦", "K"), ("♦", "A")]
结果很长,我也有注释在代码里,就不上截图了。
使用forEach进行迭代
var numbers = [1, 2, 3, 4]
for value in numbers{
print(value)
}
numbers.forEach { (value) in
print("forEach: \(value)")
}
for循环和forEach在某些情况能达到同样的效果,而且forEach代码更简洁。不过
for 循环和 forEach 有些细微的不同,值得我们注意。比如,当一个 for 循环中有 return 语句时,将它重写为 forEach 会造成代码行为上的极大区别。
再思考一下下面这个简单的例子:
(1..<10).forEach { number in
print(number)
if number > 2 { return }
}
你可能一开始还没反应过来,其实这段代码将会把输入的数字全部打印出来。return 语句并不会终止循环,它做的仅仅是从闭包中返回。
书里有个更复杂的例子的对比,我没整理,到是觉得里面这个方法可以仔细研究一下
numbers.indices //CountableRange(0..<4)
extension Array where Element: Equatable {
func index(of element: Element) -> Int? {
print(element)
for idx in self.indices where self[idx] == element {
return idx
}
return nil
}
}
numbers.index(of: 3) //2 下标2
numbers.index(of: 4) //3 下标3
numbers.index(of: 5) //nil 找不到这个数字的下标
var strs = ["一","二","三"]
strs.index(of: "三") //2 下标2
strs.index(of: "四") //nil 找不到这个字符串的下标
题外话:看到跌代,突然就想了解下这个名词,然后看到递归和迭代,然后看到一个好的例子
//迭代实现斐波那契数列
func fab_iteration(index:Int) -> Int{
guard index > 0 else {
return 0
}
if index == 1 || index == 2 {
return 1
}else{
var f1 = 1
var f2 = 1
var f3 = 0
for num in 0..<index - 2 {
f3 = f1 + f2
f1 = f2
f2 = f3
}
return f3
}
}
fab_iteration(index: 0)
fab_iteration(index: 1)
fab_iteration(index: 2)
fab_iteration(index: 3)
fab_iteration(index: 4)
fab_iteration(index: 11)
fab_iteration(index: 12)
fab_iteration(index: 13)
//递归实现斐波那契数列
func fab_recursion(index:Int) -> Int{
guard index > 0 else {
return 0
}
if index == 1 || index == 2{
return 1
}else{
return fab_recursion(index: index - 1) + fab_recursion(index: index - 2) //递归求值,自己调用自己
}
}
fab_recursion(index: 0)
fab_recursion(index: 1)
fab_recursion(index: 2)
fab_recursion(index: 3)
fab_recursion(index: 4)
fab_recursion(index: 11)
fab_recursion(index: 12)
fab_recursion(index: 13)
看起来递归更简洁了,但是我上截图你们就知道区别了。
迭代运行了33次,递归466次,这个就相差太多了,也就是递归吃了你超级多的内存。
总结就是:能自己内部实现,麻烦一点也要用迭代。实在无法自己内部实现才用递归。
数组类型
切片
var fibs = [0, 1, 1, 2, 3, 5, 8]
type(of: fibs) //Array<Int>.Type
let slice = fibs[1..<fibs.endIndex]
slice //[1, 1, 2, 3, 5, 8]
type(of: slice) //ArraySlice<Int>.Type 返回数组的一个切片(slice)
切片类型只是数组的一种表示方式,它背后的数据仍然是原来的数组
也就是相对于原数组,它只不过是从第一个数据开始展示,本质上任然是之前的数组
如果要转换为数组,就需要构建成数组
//构成数组
let slicceArray = Array(fibs[1..<fibs.endIndex])
type(of: slicceArray) //Array<Int>.Type
2017.08.07
字典
在一个字典中,每个键都只能出现一次。通过键来获取值所花费的平均时间是常数量级的 (作为对比,在数组中搜寻一个特定元素所花的时间将与数组尺寸成正比)。和数组有所不同,字典是无序的,使用 for 循环来枚举字典中的键值对时,顺序是不确定的。
enum Setting {
case text(String)
case int(Int)
case bool(Bool)
}
let defaultSettings: [String:Setting] = ["Airplane Mode": Setting.bool(true), "Name": Setting.text("My iPhone"), "PhoneNumber":Setting.int(110)]
defaultSettings["Name"] //text("My iPhone")
defaultSettings["test"] //nil
var defaultSettings2 = defaultSettings
defaultSettings2.removeValue(forKey: "Name") //text("My iPhone") 返回了被删除的值
defaultSettings2["Name"] //text("My iPhone")
defaultSettings2.updateValue(Setting.int(12110), forKey: "PhoneNumber") //int(110) 返回了更新前的值
defaultSettings2["PhoneNumber"] //int(12110)
defaultSettings2.updateValue(.text("new"), forKey: "new value") //nil 如果之前没值则返回nil
defaultSettings2["new value"] //text("new") 新的值
有用的字典扩展
//合并两个字典,通过循环添加内容
extension Dictionary {
mutating func merge<S>(_ other: S) where S: Sequence, S.Iterator.Element == (key: Key, value: Value) {
for (k, v) in other {
self[k] = v
}
}
}
defaultSettings2.merge(["1":.text("111")])
var set = ["a":"1","b":"2"]
set.merge(["c":"3", "d":"4"])
//从序列创建数组的方法
extension Dictionary {
init<S:Sequence>(_ sequence: S) where S.Iterator.Element == (key: Key, value: Value){
self = [:]
self.merge(sequence)
}
}
let se = Dictionary(set)
se
type(of: se)
//字典的map方法,会返回数组
let seMap = se.map { (key: String, value: String) -> (String, String) in
if key == "a"{
return ("is a :" + key, "is 1 :" + value)
}else{
return (key + "lala", value + "map")
}
}
//字典调用map放回会返回数组
type(of: seMap) //Array<(String, String)>.Type
//返回字典的自定义map函数
extension Dictionary {
func mapValues<NewValue>(transform: (Value) -> NewValue)
-> [Key:NewValue] {
return Dictionary<Key, NewValue>(map { (key, value) in
print("key:\(key) value:\(transform(value))")
return (key, transform(value))
})
}
}
let settingsAsStrings = defaultSettings2.mapValues { setting -> String in
switch setting {
case .text(let text):
return text
case .int(let number):
return String(number)
case .bool(let value):
return String(value)
}
}
settingsAsStrings //["PhoneNumber": "12110", "new value": "new", "Airplane Mode": "true", "1": "111"]
//简单示例
let mama = se.mapValues { (str) -> String in
return str + " mapValues"
}
mama //["b": "2 mapValues", "a": "1 mapValues", "d": "4 mapValues", "c": "3 mapValues"]
代码注释很详细,而且有之前的数组方法做铺垫,理解起来容易多了。
2017.08.08
Set
集合是一组无序的元素,每个元素只会出现一次。
//集合
let naturals: Set = [1, 2, 3, 2]
naturals //[2,3,1]
naturals.contains(3) //true
naturals.contains(2) //true
naturals.contains(4) //false
集合代数
//一个集合中求另一个集合的补集:
let iPods: Set = ["iPod touch", "iPod nano", "iPod mini","iPod shuffle", "iPod Classic"]
let discontinuedIPods: Set = ["iPod mini", "iPod Classic"]
let currentIPods = iPods.subtracting(discontinuedIPods)
currentIPods // ["iPod shuffle", "iPod nano", "iPod touch"]
//求两个集合的交集
let touchscreen: Set = ["iPhone", "iPad", "iPod touch", "iPod nano"]
let iPodsWithTouch = iPods.intersection(touchscreen)
iPodsWithTouch // ["iPod touch", "iPod nano"]
//求两个集合的并集
var discontinued: Set = ["iBook", "Powerbook", "Power Mac"]
discontinued.formUnion(discontinuedIPods)
discontinued // ["iBook", "iPod mini", "Powerbook", "Power Mac", "iPod Classic"]
索引集合
var indices = IndexSet()
indices.insert(integersIn: 1..<5)
indices.insert(integersIn: 11..<15)
let indicesNumber = indices.filter{ $0 % 1 == 0 }
indicesNumber //[1, 2, 3, 4, 11, 12, 13, 14]
let evenIndices = indices.filter { $0 % 2 == 0 }
evenIndices // [2, 4, 12, 14]
闭包使用集合
extension Sequence where Iterator.Element: Hashable{
func unique() -> [Iterator.Element]{
var seen: Set<Iterator.Element> = []
return filter {
if seen.contains($0){
return false
}else{
seen.insert($0)
return true
}
}
}
}
let uni = [1,2,3,3,5,5,6,7,7,13,12,0].unique() //[1, 2, 3, 5, 6, 7, 13, 12, 0]
type(of: uni) //Array<Int>.Type
//示例
var test:Set<Int> = []
type(of: test) //Set<Int>.Type
let uni1 = [1,2,3,3,5,5,6,7,7,13,12,0].filter { (num) -> Bool in
if test.contains(num){
print("false:\(num)")
return false
}else{
print("true:\(num)")
test.insert(num)
return true
}
}
uni1 //[1, 2, 3, 5, 6, 7, 13, 12, 0]
type(of: uni1) //Array<Int>.Type
Range
// 0 到 9, 不包含 10
let singleDigitNumbers = 0..<10
// 包含 "z"
let lowercaseLetters = Character("a")...Character("z")
范围看起来很自然地会是一个序列或者集合类型,但是可能出乎你的意料,它并非这两者之一 - 至少不是所有的范围都是序列或者集合类型。
在标准库中,现在有四种范围类型。它们能够被归类到一个 2x2 的矩阵中:
---- | 半开范围 | 闭合范围 |
---|---|---|
元素满足 Comparable | Range | ClosedRange |
元素满足 Strideable (以整数为步长) | CountableRange | CountableClosedRange |
只有半开范围能够表达空区间的概念 (当范围的上下边界相等时,比如 5..<5)。
只有闭合范围能够包含它的元素类型所能表达的最大值 (比如 0...Int.max)。半开范围总是最少会有一个值比范围所表达的只要大。
Range后续的内容感觉超级底层,我决定以后再看看好了,现在看也看不懂。
发现一个文章写完太长了,分章节记载分享好了,也方便有需要的人根据需求查看
《Swift进阶》王巍译——集合类型协议