Swift 3.0 项目升级实战
- dispatch_once方法废弃,可以使用如下两种方式实现:
public extension DispatchQueue {
private static var _onceTracker = String
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: String, block:()->Void) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
if _onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}
第二种方式是苹果推荐的方式(xCode自动将swift升级时,几乎所有的dispath_once方法都会自动转换成下面这种形式),代码如下:
var t: SomeObject?
private static var __once: () = {
//此处做一次性操作
t = SomeObject()
}()
如果仅仅是需要单例模式的话可以通过如下代码实现(static let 标记的常量,为线程安全的,并且是懒加载,只赋值一次):
class SingleInstanceTest {
static let sharesdInstance = SingleInstanceTest()
private override init() {
// 注册通知等关初始化操作
}
}
- CGSize、CGPoint、CGRect 的make方法取消,需要使用CGSize(width: 1, height: 1)等方式创建
- 很多原来的方法如systemFontOfSize都将最后的单词变为方法的外部参数名:UIFont.systemFont(ofSize: kGlobalFontSize_H)
- UIColor等类型的类方法都变为静态属性:
UIColor.redColor() -> UIColor.redColor
UIColor.clearColor() -> UIColor.clearColor
- 绘制直线等CoreGraphics操作由之前的CGPathMoveToPoint、CGPathAddLineToPoint变为path.move、path.addLine等,参数不变(大多数CoreGraphics的方法调用都显得更swift化,可以查看相关文档)。
- 如果方法带有返回值,例如
func sum(a: Int , b: Int) -> Int {
return a + b
}
如果不需要返回值可以通过 _ = sum(a: 5 , b: 6)来避免警告。
- String与NSString必须显示转换例如,如下代码会报错:
func say(a: NSString) {
print(a)
}
var d : String = "hello"
say(a: d)
此时需要将变量d显式转换为NSString
- NSDateFormatter、NSNumberFormatter、NSBundle等被彻底废弃,需要使用DateFormatter和NumberFormatter、Bundle等代替,同时苹果推出Date、Data、Calendar等swift类型(去OC化正在紧张进行中,如果苹果推出新的swift类型可以替代OC,可能会将原OC类型禁用掉,那么为了以后swift升级降低代码的修改量,建议使用swift的类型)。
- GCD更swift化,swift3.0中可以使用DispatchQueue获取主线程、global线程或者创建单独的线程,线程任务的提交可以通过DispatchQueue的asyn、syn等(注意:通过指定线程优先级获取的global线程方法已经将要废弃,可以通过使用global(qos:_)->DispatchQueue创建)。
- swift 3.0中对数组的操作例如:插入、删除、判断是否包含的相关方法更简洁:insert(: , at:)、remove(at: )、contains(: )
- swift 3.0中获取类的名称(类型)需要通过 type(of: _)方法获得,如下代码所示:
class Person {
class func className() -> String {
return String(describing: self)
}
func typeS () -> String {
return String(describing: type(of: self))
// type(of: _)相当于dynamicType
}
func typeO () -> Person.Type {
let c = type(of: self)
return c
}
}
var c = Person().typeS()
var d = Person.className()
var o = String(describing: Person().typeO())
- swift3.0 中 UIControl的状态(Normal、Disable、Highlight等)被封装到一个结构体中,并且该结构体实现了OptionSet协议,该协议用于掩码运算(注意:xCode会自动将.normal状态变为UIControlState(),可以修改为.normal)。
- swift3.0 的取余运算:在xcode转换swift版本时,会将取余运算符% 变为truncatingRemainder(dividingby:_),进入文档看了一下swift3.0的标准库中多了许多的算数方法。
- swift3.0通知变更,通知中心由NSNotificationCenter 变为 NotificationCenter,并且post与addObject方法中的key参数由NSNotification.Name代替,变化如下所示:
// swift 2
NSNotificationCenter.defaultCenter().postNotificationName("kLogoutNotification", object: nil)
// swift 3
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "kLogoutNotification"), object: nil)
- swift 3.0方法外参的变化,第一个参数也需要指定一个外参数(外参数为调用处显示的参数,内参数是在函数内使用的参数),如果不指定外参数,需要有通配符替代,代码如下所示:
// swift 2 的写法,方法第一个外参数,可以不需要显示给出
func Say(p: Int , two: Int , _ three: Int) {
print("(p)")
print("(two)")
print("(three)")
}
// swift 3 的写法,必须显示给出第一个参数的外参数,否则编译报错
func Say(_ p: Int , two: Int , _ three: Int) {
print("(p)")
print("(two)") // 第二个参数two可同时做内参数与外参数
print("(three)") // 通配符代替第三个参数,调用处可以不显示外参数
}
Say(p: 0, two: 1, 2) //
- swift 3.0 枚举的变化,xCode升级swift时,会将所有的枚举类型的值得首字母变为小写(不强制,最好还是按照这个规则进行编码),代码如下:
enum SchoolRange: Int {
case default
= 1
case province
case city
case nation
func title() -> String {
switch self {
case .province:
return "省级"
case .city :
return "市级"
case .nation:
return "国家级"
default:
return "县级"
}
}
}
- swift3.0中的泛型可以获取OC中泛型的类型,如果在OC中使用了泛型,那么在swift与OC的混编中,swift可以推测泛型占位符的类型,如下代码所示。
// OC 泛型定义
// OC 代码
@interface Person<T>
{
id _typeCode;
}
- (T)typeCode;
@end
@interface Student
- (Person<School>)typeCodeSchool;
@end
// swift 2.2
var s: Student = Student()
var c = s.typeCodeSchool() //c的类型为Person
// swift 3.0
var s: Student = Student()
var c = s.typeCodeSchool() //c的类型为 Person<School>
- private 和 fileprivate:在swift3中private的作用域缩小了,仅能在当前的大括号内被访问,而fileprivate仅能在当前文件中被访问,如下代码所示
// swift 2中 同一文件可以访问private标记的变量
class Person {
private var name: String!
}
class Student {
func testPer(p: Person) {
p.name = "Hello" // swift 2中private标记的变量,可以被同一文件内的任何类、方法访问
}
}
// swift 3中private标记的变量只能被{}中的方法等访问
class Person {
private var name: String!
fileprivate var age: Int!
}
class Student {
func testPer(p: Person) {
p.age = 32 // swift3中fileprivate标记的变量可以在本文件中的任何位置访问
p.name = "Hello" // 此处编译错误
}
}
- swift3.0中的Bool类型的变量名前都加上了is如isEnable、isHidden等
- swift3.0中新增open关键字,被open标记的变量可以被不同的module的类继承、重写,public标记的变量或者类可以被当前module的类继承、重写,默认情况下internal不可以被继承或者重写。
- swift3.0中的逃逸闭包,如果闭包被作为参数传递到函数时,该闭包不需要立即执行而是需要等某些线程完成任务之后再执行,那么需要在该闭包前加上@escaping,否则编译器报错。如下代码所示:
//以下函数是当动画页面动画结束之后执行一个闭包
func animationAfter(completion: @escaping () -> Void) {
UIView.animate(withDuration: 0.3, animations: {
// do some animation
}) { (isCom) in
completion()
}