Public 和 Open 都用来声明函数或者类的作用域
Public 可以被其他模块访问, Open 权限大于Public 不仅可以访问还可以继承。
- fileprivate 文件内可见
- final 任何地方都不能继承。
static 和 class
static 和 class 在类中的应用,都可用来修饰 类方法 但是static修饰的类方法不能被继承 用class 修饰的类方法是可以被继承的
- class func classMethod() {} //可以被继承
- static func staticMethod() {} // 不可以被继承 有包含final关键字的特性 Error:Class method overrides a ‘final’ class method.
- 在protocol里要用class来统一修饰 class ,struct 和enum在
类型域
上的方法,但在struct和enum具体实现时还是用static来修饰
class Star {
class func spin() {}
static func illuminate() {}
}
class Sun : Star {
override class func spin() {
super.spin()
}
override static func illuminate() { // error: class method overrides a 'final' class method
super.illuminate()
}
}
@escaping 和 @noescaping
- @escaping 逃逸闭包 :当闭包作为函数参数时,在函数执行完后才被调用,调用的地方超过了这个函数的范围就是逃逸闭包
func doWorkAsync(block: @escaping ()->()) {
DispatchQueue.main.async {
block()
}
}
- @noescaping 非逃逸闭包:才函数结束前就被调用。线性布局约束的闭包Cartography
func doWork(block: ()->()) {
block()
}
doWork {
print("work")
}
- Swift 3.0 中默认都是@noescaping非逃逸闭包,如果要用到逃逸闭包必须要在
闭包参数前声明
@escaping - 对于 @escaping 类型的闭包 如果引用了self,编译是要强制写出来self的。已提醒程序员这里引用了self。
class S {
var foo = "foo"
func method1() {
doWork { //非逃逸闭包 作用域仅限于method1函数内,不担心强引用self
print(foo)
}
foo = "bar"
}
func method2() {
doWorkAsync { //逃逸闭包 强引用self,如果用 [weak self] 则有可能闭包被调用时 self被释放
print(self.foo)
}
foo = "bar"
}
}
S().method1() // foo
S().method2() // bar
Swift中Closure避免引用循环的办法
Swift 中闭包避免引用循环的办法:
第一种:
let shop : LRShop = LRShop()
weak var weakShop = shop
shop.myBlock = {(str : String) -> () in
weakShop?.string = str
print((weakShop?.string)!)
}
shop.myBlock!(str: "哈喽,你好!")
第二种:
let shop : LRShop = LRShop()
shop.myBlock = {[unowned shop] (str : String) -> () in
shop.string = str
print(shop.string!)
}
shop.myBlock!(str: "哈喽,你好!")
第三种
let shop : LRShop = LRShop()
shop.myBlock = {[weak shop] (str : String) -> () in
shop?.string = str
print((shop?.string)!)
}
shop.myBlock!(str: "哈喽,你好!")
第四种
let shop : LRShop = LRShop()
shop.myBlock = {[weak shop] (str : String) -> () in
//强引用
let strongShop = shop;
//时间设置
let time: NSTimeInterval = 2
//GCD:延迟2秒
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
Int64(time ` Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
//赋值
strongShop?.string = str
//打印输出
print((strongShop?.string)!)
}
}
shop.myBlock!(str: "哈喽,你好!")
Swift里面closure默认是以引用的方式保存作用域的对象,加了capture捕获列表则表示以值拷贝的方式保存捕获列表中的对象。
var x = 42
let f = {
// [x] in //如果取消注释,结果是42
print(x)
}
x = 43
f() // 结果是43
但是如果写在参数列表里面,那么就会像函数的参数一样,重新生成一个拷贝。这点与OC完全相反
注意 被可选值类型封装
class A {
var name: String = "A"
var block: (() -> ())?
//其他方法
}
var a: A? = A()
var block = {
print(a?.name)
}
a?.block = block
a = nil
block()
我们先创建了可选类型的变量a,然后创建一个闭包变量,并把它赋值给a的block属性。这个闭包内部又会截获a,那这样是否会导致循环引用呢?
答案是否定的。虽然从表面上看,对象的闭包属性截获了对象本身。但是如果你运行上面这段代码,你会发现对象的deinit方法确实被调用了,打印结果不是“A”而是“nil”。
这是因为我们忽略了可选类型这个因素。这里的a不是A类型的对象,而是一个可选类型变量,其内部封装了A的实例对象。闭包截获的是可选类型变量a,当你执行a = nil时,并不是释放了变量a,而是释放了a中包含的A类型实例对象。所以A的deinit方法会执行,当你调用block时,由于使用了可选链,就会得到nil,如果使用强制解封,程序就会崩溃。
如果想要人为造成循环引用,代码要这样写:
var block: (() -> ())?
if true {
var a = A()
block = {
print(a.name)
}
a.name = "New Name"
}
block!()
处理引用循环时用 weak 还是 unowned ?
用weak是相对安全的做法,只有在 在闭包和引用对象 他们的生存周期同步一致时,可以用unowned 。因为很难保证在闭包执行时引用对象是否还存在,
避免引用循环的最佳实践
extension Optional {
func withExntendedLifetime( _ body: (wrapped)->void ) {
if let value = self {
body(value)
}
}
}
DispatchQueue.global().async { [weak self] in
self.withExtendedLifetime {
print("Before doSomething : \($0) ")
usleep(1000)
print("After doSomething : \($0) ")
}
}
- 即在closure的捕获列表中用weak
eg:[weak self]
引用对象,避免引用循环。 - 又能在closure执行时保证对象
eg:self
一直存在。 - withExtendedLifetime(self,{ _ in doSomething() }) 这个函数接受一个对象和一个闭包。作用是在闭包执行完前。一直保存第一个对象不被销毁。
- 用 $0 参数来代替 self!
map 和 flatmap
Sequence的map
简单映射
let arr = [1, 2, 4]
let brr = arr.map { "No." + String($0) } // brr = ["No.1", "No.2", "No.4"]
Sequence的flatmap
降维展开
let arr = [[1, 2, 3], [6, 5, 4]]
let brr = arr.flatMap { $0 } // brr = [1, 2, 3, 6, 5, 4]
过滤 经过转换后为nil 的结果
let arr: [Int?] = [1, 2, nil, 4, nil, 5]
let brr = arr.flatMap { $0 } // brr = [1, 2, 4, 5]
let images = (1...6).flatMap {
UIImage(named: "imageName-\($0)")
}
Optional的map
不解包的情况下可以直接运算
let b2 = a2.map { (a : Int) -> Int in
return a ` 2
}
之前:
var date: NSDate? = ...
var formatted = date == nil ? nil : NSDateFormatter().stringFromDate(date!)
之后:
var date: NSDate? = ...
var formatted = date.map(NSDateFormatter().stringFromDate)
Optional的flatMap
不解包的情况下可以直接运算 + 自动过滤 转换后为nil的结果
let s: String? = "abc"
let v = s.flatMap { (a: String) -> Int? in
return Int(a)
}
Swift 的错误处理
同步API 用 throw 强制使用 do try catch
do {
try d.write(toFile: "Hello", options: [])
} catch let error as NSError {
print ("Error: \(error.domain)")
}
异步API 使⽤泛型枚举。
enum Result<T> {
case Success(T)
case Failure(NSError)
}
func doSomethingParam(param:AnyObject) -> Result {
//...做某些操作,成功结果放在 success 中
if success {
return Result.Success("成功完成")
} else {
let error = NSError(domain: "errorDomain", code: 1, userInfo: nil)
return Result.Error(error)
}
}
let 和 var
当let修饰class时, 引用不能被重新赋值,但引用的对象属性可以
let view2 = UIView()
view2.alpha = 0.5 // compile ok
view2 = UIView() // compile error
当let修饰struct时,调用struct的mutating方法是不被允许的
public struct ThermometerStruct {
private(set) var temperature: Double = 0.0
public mutating func registerTemperature(temperature: Double) {
self.temperature = temperature
}
}
let thermometerStruct = ThermometerStruct()
thermometerStruct.registerTemperature(56.0) // compiler error