Swift 3.0 笔记 (1)

2017/03/02

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的捕获列表中用weakeg:[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

Post Directory