本文共 8999 字,大约阅读时间需要 29 分钟。
Swift是苹果于2014年WWDC(苹果发布大会)发布的新开发语言,可与Object-C共同运行于MAC OS 和iOS平台,用于搭建基于苹果平台的应用程序。
swift即是面向对象的,又是函数式的编程语言。说swift是面向对象的语言,是因为swift支持类的封装、继承、多态。说swift是函数式编程语言,是因为swift支持map、reduce、filter,flatMap这些函数式的方法。swift引入了在Objective-C中没有的一些高级数据类型,例如:
1.tuples(元组),可以使你创建和传递一组数值。 2.可选项类型(Optionals),用于处理变量值不存在的情况。两个概念:动态派发和静态派发
OC中的方法都是动态派发,Swift中的方法分为为静态派发和动态派发。 动态派发:指的是方法在运行时才找具体实现,swift中的动态派发和OC中的动态派发是一样的。 静态派发:静态派发是指在运行时调用方法不需要查表,直接调到转方法的代码中执行。 静态派发的特点: 静态派发更高效,因为静态派发免去了查表操作。 静态派发的条件是方法内部的代码必须对编译器透明,且在运行时不能被更改,这样编译器才能帮助我们。 swift中的值类型不能被继承,也就是说值类型的方法实现不能被修改或者被复写,因此值类型的方法满足静态派发。值类型相比引用类型,最大的优势在于内存使用的高效,值类型在栈上操作,引用类型在堆上操作。
栈上的操作仅仅是单个指针的上下移动,而堆上的操作则牵涉到合并、移位、重新链接等,也就是说swift这样设计,大幅减少了堆上的内存分配和回收的次数。同时写时复制又将值传递和复制的开销降到最低。 String,Array,Dictionary设计成值类型,也是为了线程安全考虑,通过swift的let设置,使得这些数据达到了真正意义上的“不变”,它也从根本上解决了多线程中内存访问和操作顺序的问题。设计成值类型还可以提升API的灵活度。1.NSArray内存中存储地址连续,而NSSet不连续
2. NSSet效率高,内部使用hash查找,NSArray通过遍历(比如你要存储元素A,一个hash算法直接就能直接找到A应该存储的位置;同样,当你要访问A时,一个hash过程就能找到A存储的位置。而对于NSArray,若想知道A到底在不在数组中,则需要便利整个数组,显然效率较低了。 3. NSSet通过anyObject访问元素,NSArray通过下标访问。在OC和Swift中字符串的区别
在OC中字符串类型是NSString ,在Swift中字符串类型是String 使用String的原因 String是一个结构体,性能更高 NSString 是一个OC对象,性能略差 String 支持直接便利 Swift提供了String和NSString之间的无缝转换扩展, 全局函数
//定义一个set集合 let setA:Set = [1,2,3,4,4]//顺序可能不一样,但同一种元素只有一个值 let setB:Set = [1,3,5,7,9] //取并集 A|B [1,2,3,4,5,7,9] let setUnion = setA.union(setB) //取交集 A&B [1,3] let setIntersect = setA.intersection(setB) //取差集 [2,4] let setRevers = setA.subtracting(setB) //取对称差集 A XOR B = A-B | B-A [2,4,5,7,9] let setXor = setA.symmetricDifference(setB)
map用于映射,作用是生成一个新数组,遍历原数组,将每个元素拿出来做一些变换后放入到新的数组中
filter用于过滤,可以筛选出想要的元素 reduce 合并map 的简单用法:
我们可以使用一个for-in 循环来计算数组中每一个元素的平方
let values = [2,4,5,7] var squares:[Int] = [] for value in values { squares.append(value*value) }
这段代码是有效的,但是显得有些冗长,我们同样需要定义一个 squares 变量,现在来比较使用map的做法:
let values = [2,4,5,7] let squares = values.map({(value:Int) -> Int in return value*value })
省略returen
let squares2 = values.map {value in value * value}
in 关键字的作用是将闭包的参数与函数体分开。你可以使用参数编号将代码简化得更彻底:
let squares = values.map { $0 * $0 }
Filter的用法:
filter函数会遍历一个集合,并返回一个Array,其中包含了集合中满足过滤条件的元素。let digits = [1,4,10,15] let even = digits.filter { $0%2==0} //[4,10]
Reduce 用法:
使用reduce来组合集合中的所有元素并返回一个非集合类型的值 reduce函数有两个参数,一个初始值,另一个为组合闭包,比如求以下数组中的所有元素与初始值10的和:let items = [2.0,4.0,5.0,7.0]let total = items.reduce(10.0,combine: +)// 28.0//译者注:Xcode 8.0中Swift语法更为简洁let items = [2.0,4.0,5.0,7.0]let total = items.reduce(10.0,+)// 28.0
+操作符同样适用于整合字符串数组:
let codes = ["abc","def","ghi"]let text = codes.reduce("", combine: +)// "abcdefghi"//译者注:Xcode 8.0中Swift语法更为简洁let codes = ["abc","def","ghi"]let text = codes.reduce("",+)// "abcdefghi"
因为组合参数是一个闭包,所以你可以适用尾随闭包语法来编写reduce:
let names = ["1lan","brian","charlie"] let csv = names.reduce("===") { (text, name) -> String in "\(text),\(name)" }
FlatMap和compactMap的用法:
FlatMap在swift4.1已经废弃,用compactMap替代,最简单的用法是如同它的名字所描述的那样将一个二维数组拆开展平,也可以拆开展平一个多维数组,并且这个多维数组的子数组可以经过处理,最终返回一个包含所有结果的一个一维数组直接将下面二维数组展开let collections = [[5,2,7],[4,8],[9,1,3]]let flat = collections.flatMap { $0 }// [5, 2, 7, 4, 8, 9, 1, 3]
他可以判断集合中的不可选值,并将不可选值移除集合:
遍历一个二维数组并返回一个元素都为偶数的一维数组let people: [String?] = ["Tom",nil,"Peter",nil,"Harry"]let valid = people.flatMap { $0}// ["Tom", "Peter", "Harry"]
利用compactMap与map函数求二维数组的每一个Int元素的平方,返回一个Array
let collections = [[5,2,7],[4,8],[9,1,3]]let allSquared = collections.flatMap { $0.map { $0 * $0 } }
利用compactMap与reduce函数求出二维数组中每个子元素的元素之和,返回一个包含结果的Array:
let collections = [[5,2,7],[4,8],[9,1,3]] let sums = collections.compactMap({ $0.reduce(0, +)})
我们可以将这些函数链接起来使用,例如求数组中大约等于7的元素之和,我们可以先用filter筛选,其次再用reduce求和
let marks = [4,5,8,2,9,7] let totalPass = marks.filter({ $0>=7}).reduce(0, +)
NSHashTable是NSSet的通用版本,对元素弱引用,可变类型;可以在访问成员时copy。
NSMapTable是NSDictionary的通用版本,对元素弱引用,可变类型,可以在访问成员时copy (注:NSHashTable与NSSet的区别:NSHashTable可以通过option设置元素弱引用/copyin,只有可变类型,但是添加对象的时候NSHashTable耗费时间是NSSet的两倍。NSMapTable与NSDictionary的区别:同上)默认情况下,不能在实例方法中修改值类型的属性。若在放在实例中使用mutating关键字,不仅可以在实例方法中修改值类型的属性,而且会在方法实现结束时将其写原始结构。
使用 mutating 关键字修饰方法是为了能在该方法中修改 struct 或是 enum 的变量 首先,先定义一个protocolprotocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust()}
在上面,定义了一个ExampleProtocol,接下来我们写一个class来遵守这个协议, // 在 class 中实现带有mutating方法的接口时,不用mutating进行修饰。因为对于class来说,类的成员变量和方法都是透明的,所以不必使用 mutating 来进行修饰。
class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class" var anotherProperty: Int = 110func adjust() { simpleDescription += " Now 100% adjusted" }}
在 struct中实现协议ExampleProtocol
struct SimpleStruct: ExampleProtocol { var simpleDescription: String = "A simple structure" mutating func adjust() { simpleDescription += "(adjusted)" }}
在enum中实现协议ExampleProtocol同上
如果将ExampleProtocol中修饰方法的mutating去掉,编译器会报错说没有实现protocol。如果将struct中的mutating去掉,则会报错不能改变结构体的成员。autoclosure(自动闭包)就是一个自动构建的闭包。该闭包不接受任何参数,不含任何处理,只返回一个值。
在类的声明中使用final关键字声明类、属性、方法和下标。final声明的属性、方法和下标不能被重写。
如果只是限制一个方法或属性被重写,只需要在该方法或者属性前加一个 final. 如果需要限制整个类无法被继承, 那么可以在类名之前加一个final。swift中有两种参数传递方式:
1.值传递:值传递的是参数的一个副本,这样再调用参数的过程中不会影响原始数据。 2.指针传递:指针传递会把参数本身引用(内存地址)传递过去,在调用的过程会影响原始数据。 在swift众多数据类型中,只有class是指针传递,其余的如Int,Float,Bool,Character,Array,Set,enum,struct全是值传递。 inout关键字让值传递以指针方式传递class JCRegisterViewController: UIViewController { var value1 = 50 var value2 = 50 override func viewDidLoad() { super.viewDidLoad() increment1(value: self.value1, length: 10) increment2(value: &self.value2, length: 10) print(self.value1) //60 print(self.value2) //60 } func increment1(value:Int,length:Int) -> Void { self.value1 = value+length; } func increment2(value: inout Int,length:Int) -> Void { value = value+length; }}
typealias
typealias 是用来为已存在的类型重新定义名字的,通过命名,可以使代码变得更加清晰。typealias Success = (_ data: String) -> Voidtypealias Failure = (_ error: String) -> Void func request(_ url: String, success: Success, failure: Failure) { // do request here ....}
typealias与泛型
typealias是单一的,也就是说你必须指定将某个特定的类型通过typealias赋值为新名字,而不能将整个泛型类型进行重命名,下面这样的命名都是无法通过编译的。
class Person{ }typealias Woker = Persontypealias Worker = Person
associatedtype关联类型
你可以通过associatedtype关键字来指定关联类型。比如协议声明更新cell的方法://模型struct Model { let age: Int} //协议,使用关联类型protocol TableViewCell { associatedtype T func updateCell(_ data: T)} //遵守TableViewCellclass MyTableViewCell: UITableViewCell, TableViewCell { typealias T = Model func updateCell(_ data: Model) { // do something ... }}
Error是一个协议,swift中的Error都是enum,可以转NSError。如果需要Error有NSError的功能,实现LocalizedError CustomNSError 协议。
open > public > interal > fileprivate > private
open: 修饰的属性或者方法在其他作用域既可以被访问也可以被继承或者重载override。
public:修饰的属性或者方法可以在其他作用域被访问,但不能在重载override中被访问,也不能在继承方法中的Extension中被访问。 internal:被修饰的属性和方法只能在模块内部可以访问,超出模块内部就不可以被访问了。(默认) fileprivate:其修饰的属性或者方法只能在当前的swift源文件里可以访问。 private:只允许在当前类中调用,不包括Extension,用private修饰的方法不可以被代码域之外的地方访问。主要集中在循环引用上面,如block、NSTime、perform selector 引用计数问题。
泛型可以使我们在程序代码中定义一些可变的部分,在运行的时候指定。使用泛型可以最大限度地重用代码、保护类型的安全。
guard
是一个要求表达式的值为true 从而继续执行的条件语句。如果表达式为false,则会执行必须提供的else分支。 且在 else 语句中一定要停止函数调用。func sayHello(numberOfTimes: Int) { guard numberOfTimes > 0 else { return }}
defer
swift鼓励用尽早返回错误来代替嵌套if的处理方式,defer关键字为此提供了安全又简单的处理方式:声明一个block,当前代码执行的闭包退出时会执行该block。 经常defer 如果在同一个作用域内使用多个defer语句,他们会根据出现顺序反过来执行。注意:嵌套defer先执行外层,后执行内层。func test() -> Void { print(5) defer { print(3) } defer { defer { print(2) } print(1) } defer { print(4) } print(6) } 输出结果:5 6 4 1 2 3
1.Alamofire:Swift中著名的网络请求库
2.Moya:著名的Alamofire封装,让网络请求看起来更加的优美,更有利于阅读与迭代 3.MBProgressHUD: 进度条,弹出框,OC写的库 4.Kingfisher: 加载网络图片,类似SDWebImage 5.数据模型解析 5.1 HandyJSON 是阿里巴巴开源的model的映射库。使用方式类似OC中的MJExtention 5.2 SwiftJSON 5.3 ObjectMapper:json解析库,需要手动写映射关系 5.4 Codable 6.IQKeyboardManagerSwift 键盘库 7.MJRefresh 上拉刷新以及下拉加载 8.SnapKit 相对布局 9.RxSwift 响应式编程在Swift语言领域的实现库 10.RxCocoa 针对Cocoa平台(iOS & Mac OS)的响应式编程库转载地址:http://fwcti.baihongyu.com/