Swift -- 8.闭包(下)

一.@convention

之前在执行vtable取出的函数地址时,使用到了@convention

用于修饰函数类型

  • 修饰Swift中的函数类型(调用C函数的时候)
  • 调用OC方法时,修饰Swift函数类型

例如在C文件中添加一个函数,函数的参数是一个回调函数

int testConvention(int sum(int, int)) {
    return sum(10, 20);
}

Swift中使用

//直接使用testConvention,编译器已经进行了优化,已经进行了转换
let result = testConvention( {
    return $0 + $1
})

print(result) // 30

//testConvention传入一个closure
let closure = { (a: Int32, b: Int32) -> Int32 in
    return a + b
}

//报错
let result1 = testConvention(closure)

此时将closure传入testConvention会报错A C function pointer can only be formed from a reference to a 'func' or a literal closure

大致的意思是,一个C函数指针只能由方法或字面闭包构成。简单来说就是类型不匹配

此时,就可以使用到@convention来进行类型转换

//@convention(c)中的c指的是后面的(Int32, Int32) -> Int32是一个c函数
let closure: @convention(c) (Int32, Int32) -> Int32 = { (a: Int32, b: Int32) -> Int32 in
    return a + b
}
let result1 = testConvention(closure)

二.defer

defer {}里的代码会在当前代码块返回的时候执行,无论当前代码块是从哪个分支 return 的,即使程序抛出错误,也会执行。

如果多个 defer 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是 先出现的后执行。

func f() {
    defer { print("First defer") }
    defer { print("Second defer") }
    print("End of function")
}

f()

//End of function
//Second defer
//First defer

使用场景1:FileHandle使用完成后需要执行closeFile()

使用场景2:类型指针,管理析构函数

let count = 2
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)

defer {
    pointer.deinitialize(count: count)
    pointer.deallocate()
}

使用场景3:我们在进行网络请求的时候,可能有不同的分支进行回调函数的执行

func netRquest(completion: () -> Void) {
    defer {
        self.isLoading = false
        completion()
    }
    guard error == nil else { return }
}

三.全局变量和局部变量捕获的区别

1.全局变量

Swift代码

var i = 10

let closure = {
    i += 10
}

closure()

IR代码

define i32 @main(i32 %0, i8** %1) #0 {
entry:
  %2 = bitcast i8** %1 to i8*
  // 将10赋值到TSI,Int64上。
  store i64 10, i64* getelementptr inbounds (%TSi, %TSi* @"$s4main1iSivp", i32 0, i32 0), align 8

  // s4mainyycfU_ 闭包的函数地址
  // s4main7closureyycvp 就是closure变量。也就是let closure = {xxx}中的closure
  // 将i8*赋值到closure(%swift.function)的第一个元素i8*上。%swift_function ---> {i8*, %swift_refcounted*}
  store i8* bitcast (void ()* @"$s4mainyycfU_" to i8*), i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7closureyycvp", i32 0, i32 0), align 8
  
  // closure的%swift_refcounted*赋值给null
  store %swift.refcounted* null, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7closureyycvp", i32 0, i32 1), align 8
  
  // %3为closure的i8*
  %3 = load i8*, i8** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7closureyycvp", i32 0, i32 0), align 8
  
  // %4为closure的%swift_refcounted*
  %4 = load %swift.refcounted*, %swift.refcounted** getelementptr inbounds (%swift.function, %swift.function* @"$s4main7closureyycvp", i32 0, i32 1), align 8
  
  // 引用计数+1
  %5 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %4) #1

  // i8*强转为void (%swift.refcounted*)*
  %6 = bitcast i8* %3 to void (%swift.refcounted*)*

  // 执行i8*的函数
  call swiftcc void %6(%swift.refcounted* swiftself %4)

  // 引用计数-1
  call void @swift_release(%swift.refcounted* %4) #1
  ret i32 0
}

// 闭包的调用
define internal swiftcc void @"$s4mainyycfU_"() #0 {
entry:
  %access-scratch = alloca [24 x i8], align 8
  %0 = bitcast [24 x i8]* %access-scratch to i8*
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %0)
  call void @swift_beginAccess(i8* bitcast (%TSi* @"$s4main1iSivp" to i8*), [24 x i8]* %access-scratch, i64 33, i8* null) #1

  // 从TSI(全局变量)中取值之前存放的值,这里也就是10
  %1 = load i64, i64* getelementptr inbounds (%TSi, %TSi* @"$s4main1iSivp", i32 0, i32 0), align 8

  // 这里执行10与10做了一个加法
  %2 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %1, i64 10)

  %3 = extractvalue { i64, i1 } %2, 0
  %4 = extractvalue { i64, i1 } %2, 1
  %5 = call i1 @llvm.expect.i1(i1 %4, i1 false)
  br i1 %5, label %8, label %6

6:                                                ; preds = %entry
  store i64 %3, i64* getelementptr inbounds (%TSi, %TSi* @"$s4main1iSivp", i32 0, i32 0), align 8
  call void @swift_endAccess([24 x i8]* %access-scratch) #1
  %7 = bitcast [24 x i8]* %access-scratch to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %7)
  ret void

8:                                                ; preds = %entry
  call void @llvm.trap()
  unreachable
}

结论:对于全局变量,闭包是不捕获的

2.局部变量

当然我们在闭包(上)也分析过局部变量的情况,这里也再一次做简单的回顾

Swift代码

func test() {
    var i = 10
    let closure = {
        i += 10
    }
    closure()
}

test()

IR代码

define hidden swiftcc void @"$s4main4testyyF"() #0 {
entry:
  %0 = alloca %TSi, align 8
  %1 = bitcast %TSi* %0 to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %closure.debug = alloca %swift.function, align 8
  %2 = bitcast %swift.function* %closure.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
  %3 = bitcast %TSi* %0 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %3)

  //将10存入TSI
  %._value = getelementptr inbounds %TSi, %TSi* %0, i32 0, i32 0
  store i64 10, i64* %._value, align 8

  //把10存进堆区空间
  %4 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 24, i64 7) #3
  %5 = bitcast %swift.refcounted* %4 to <{ %swift.refcounted, %TSi* }>*
  %6 = getelementptr inbounds <{ %swift.refcounted, %TSi* }>, <{ %swift.refcounted, %TSi* }>* %5, i32 0, i32 1
  store %TSi* %0, %TSi** %6, align 8


  %7 = bitcast %swift.function* %closure.debug to i8*
  call void @llvm.lifetime.start.p0i8(i64 16, i8* %7)
  %closure.debug.fn = getelementptr inbounds %swift.function, %swift.function* %closure.debug, i32 0, i32 0
  store i8* bitcast (void (%swift.refcounted*)* @"$s4main4testyyFyycfU_Tf0s_nTA" to i8*), i8** %closure.debug.fn, align 8
  %closure.debug.data = getelementptr inbounds %swift.function, %swift.function* %closure.debug, i32 0, i32 1
  store %swift.refcounted* %4, %swift.refcounted** %closure.debug.data, align 8
  %8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %4) #3

  //把堆区实例对象传入到了闭包
  call swiftcc void @"$s4main4testyyFyycfU_Tf0s_nTA"(%swift.refcounted* swiftself %4)

  call void @swift_release(%swift.refcounted* %4) #3
  call void @swift_release(%swift.refcounted* %4) #3
  %9 = bitcast %TSi* %0 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %9)
  ret void
}

总结:对于局部变量,开辟堆区内存空间,存入局部变量,执行闭包时把堆区实例对象传入进去

四.捕获引用类型

Swift代码

class LGTeacher {
    var age = 10
}

func test() {
    var t = LGTeacher()
    let closure = {
        t.age += 10
    }
    closure()
}

test()

IR代码

// test函数
define hidden swiftcc void @"$s4main4testyyF"() #0 {
entry:
  %0 = alloca %T4main9LGTeacherC*, align 8
  %1 = bitcast %T4main9LGTeacherC** %0 to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)

  // %closure.debug其实就是一个%swift.function
  %closure.debug = alloca %swift.function, align 8

  %2 = bitcast %swift.function* %closure.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 16, i1 false)
  %3 = bitcast %T4main9LGTeacherC** %0 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %3)
  %4 = call swiftcc %swift.metadata_response @"$s4main9LGTeacherCMa"(i64 0) #6
  %5 = extractvalue %swift.metadata_response %4, 0

  // $s4main9LGTeacherCACycfC ---> main.LGTeacher.__allocating_init() -> main.LGTeacher
  // 创建LGTeacher实例变量
  %6 = call swiftcc %T4main9LGTeacherC* @"$s4main9LGTeacherCACycfC"(%swift.type* swiftself %5)
  %7 = bitcast %T4main9LGTeacherC* %6 to %swift.refcounted*
  %8 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %7) #3
  store %T4main9LGTeacherC* %6, %T4main9LGTeacherC** %0, align 8

  // 将创建的LGTeacher实例转化为%swift.refcounted*
  %9 = bitcast %T4main9LGTeacherC* %6 to %swift.refcounted*

  %10 = bitcast %swift.function* %closure.debug to i8*
  call void @llvm.lifetime.start.p0i8(i64 16, i8* %10)
  
  %closure.debug.fn = getelementptr inbounds %swift.function, %swift.function* %closure.debug, i32 0, i32 0

  // 将闭包表达式的函数地址存入closure.debug.fn
  store i8* bitcast (void (%swift.refcounted*)* @"$s4main4testyyFyycfU_Tf2i_nTA" to i8*), i8** %closure.debug.fn, align 8

  %closure.debug.data = getelementptr inbounds %swift.function, %swift.function* %closure.debug, i32 0, i32 1

  // 把LGTeacher存入closure.debug.data
  store %swift.refcounted* %9, %swift.refcounted** %closure.debug.data, align 8

  // LGTeacher引用计数+1
  %11 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %9) #3

  // 执行闭包的时候传入LGTeacher的实例变量
  // 至于之前声明的%closure.debug,存了函数地址及LGTeacher实例数据后,后续也再也没有使用了。具体还不清楚这里的用意
  call swiftcc void @"$s4main4testyyFyycfU_Tf2i_nTA"(%swift.refcounted* swiftself %9)

  call void @swift_release(%swift.refcounted* %9) #3
  call void @swift_release(%swift.refcounted* %9) #3
  %toDestroy = load %T4main9LGTeacherC*, %T4main9LGTeacherC** %0, align 8
  call void bitcast (void (%swift.refcounted*)* @swift_release to void (%T4main9LGTeacherC*)*)(%T4main9LGTeacherC* %toDestroy) #3
  %12 = bitcast %T4main9LGTeacherC** %0 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %12)
  ret void
}

总结:对于引用类型,已经在堆区上了,因此不需要在堆区开辟内存空间。此时的闭包只需要在传值的过程中,把这个引用类型的地址传递进去就可行了。

五.捕获多个局部变量值

Swift代码

func makeIncreasemer(_ amount: Int) -> () -> Int {
    var runningTotal = 10
    func increasmer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return increasmer
}

IR代码

define hidden swiftcc { i8*, %swift.refcounted* } @"$s4main15makeIncreasemerySiycSiF"(i64 %0) #0 {
entry:
  %amount.debug = alloca i64, align 8
  %1 = bitcast i64* %amount.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %runningTotal.debug = alloca %TSi*, align 8
  %2 = bitcast %TSi** %runningTotal.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store i64 %0, i64* %amount.debug, align 8

  // 把10存入到了开辟的堆区内存空间中
  %3 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata, i32 0, i32 2), i64 24, i64 7) #2
  %4 = bitcast %swift.refcounted* %3 to <{ %swift.refcounted, [8 x i8] }>*
  %5 = getelementptr inbounds <{ %swift.refcounted, [8 x i8] }>, <{ %swift.refcounted, [8 x i8] }>* %4, i32 0, i32 1
  %6 = bitcast [8 x i8]* %5 to %TSi*
  store %TSi* %6, %TSi** %runningTotal.debug, align 8
  %._value = getelementptr inbounds %TSi, %TSi* %6, i32 0, i32 0
  store i64 10, i64* %._value, align 8

  %7 = call %swift.refcounted* @swift_retain(%swift.refcounted* returned %3) #2

  // 分配堆区内存空间
  %8 = call noalias %swift.refcounted* @swift_allocObject(%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata.3, i32 0, i32 2), i64 32, i64 7) #2
  
  // 强转至<{ %swift.refcounted, %swift.refcounted*, %TSi }>*结构体
  %9 = bitcast %swift.refcounted* %8 to <{ %swift.refcounted, %swift.refcounted*, %TSi }>*

  // 向这个结构体的第2个元素存入%3(存有10的堆区内存空间)
  %10 = getelementptr inbounds <{ %swift.refcounted, %swift.refcounted*, %TSi }>, <{ %swift.refcounted, %swift.refcounted*, %TSi }>* %9, i32 0, i32 1
  store %swift.refcounted* %3, %swift.refcounted** %10, align 8

  // 向这个结构体的第3个元素存入%0,也就是函数的参数
  %11 = getelementptr inbounds <{ %swift.refcounted, %swift.refcounted*, %TSi }>, <{ %swift.refcounted, %swift.refcounted*, %TSi }>* %9, i32 0, i32 2
  %._value1 = getelementptr inbounds %TSi, %TSi* %11, i32 0, i32 0
  store i64 %0, i64* %._value1, align 8

  call void @swift_release(%swift.refcounted* %3) #2

  //第一个元素闭包的执行地址
  //第二个元素%8
  %12 = insertvalue { i8*, %swift.refcounted* } { i8* bitcast (i64 (%swift.refcounted*)* @"$s4main15makeIncreasemerySiycSiF10increasmerL_SiyFTA" to i8*), %swift.refcounted* undef }, %swift.refcounted* %8, 1
  
  ret { i8*, %swift.refcounted* } %12
}

还原此时闭包的数据结构

func makeIncreasemer(_ amount: Int) -> () -> Int {
    var runningTotal = 10
    func increasmer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return increasmer
}


struct ClosureData<T> {
    //闭包的地址
    var ptr: UnsafeRawPointer
    var object: UnsafePointer<T>
}

struct MultBox<T1, T2> {
    var object: HeapObject
    var box: UnsafePointer<Box<T1>>
    var value: T2
}

struct Box<T> {
    var object: HeapObject
    var value: T
}

struct HeapObject {
    var metadata: UnsafeRawPointer
    var refCount: Int
}

struct NoMeanStruct {
    let f: () -> Int
}

let f = NoMeanStruct(f: makeIncreasemer(20))

let ptr = UnsafeMutablePointer<NoMeanStruct>.allocate(capacity: 1)
ptr.initialize(to: f)
defer {
    ptr.deinitialize(count: 1)
    ptr.deallocate()
}

let p = ptr.withMemoryRebound(to: ClosureData<MultBox<Int, Int>>.self, capacity: 1) {
    $0.pointee
}

print(p.object.pointee.box.pointee.value) // 捕获的局部变量10
print(p.object.pointee.value) // 捕获的参数20

/*
 闭包已经把所有执行过程中所有的信息放入结构体里面,执行的地址,捕获的值
 
 调用的过程中其实就是从结构体拿到值,从而完成调用
 */

六.逃逸闭包

当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调用,我们就说这个闭包逃逸了。当我们声明一个接受闭包作为形式参数时,你可以在形式参数前写@escaping来明确闭包是允许逃逸的。

  • 作为函数的参数传递
  • 当前闭包在函数内部异步执行或者被存储
  • 函数结束,闭包被调用,声明周期结束
class TestEscapingClosure {
    
    var complete:((Int) -> Void)?
    
    //赋值存储
    func test(handle: @escaping ((Int) -> Void)) {
        complete = handle
    }
    
    //异步执行
    func testAsyn(handle: @escaping ((Int) -> Void)) {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
            handle(10)
        }
    }
}

1.非逃逸闭包

/*
 非逃逸闭包:闭包的生命周期是确定的
 
 注意:可选闭包表达式是逃逸闭包!!!
 func testNoEscaping(_ f: (() -> Void)?) {
     f?()
 }
 
 如果f是可选值就会变为逃逸闭包,虽然闭包生命周期与函数生命周期一致,
 但是编译器默认可选闭包表达式是逃逸闭包
 
 Swift库 pr:4905,就是解释的可选闭包表达式
 https://github.com/apple/swift/pull/4905
 */
func testNoEscaping(_ f: () -> Void) {
    f()
}

func test() {
    var age = 10

    //闭包的生命周期与函数生命周期一致
    //此时的age就不会被捕获到堆区了
    testNoEscaping {
        age += 20
    }
}

test()
  • 不会产生循环引用,函数作用域内释放
  • 编译器更多性能优化(没有retain和release)
  • 上下文的内容保存在栈上,不是堆上

2.逃逸闭包

之前的案例

func test() {
    var age = 10
    
    //逃逸闭包,个人理解为赋值导致编译器认为它是一个逃逸闭包,因此会把age捕获到堆区
    let closure = {
        age += 20
    }
    
    closure()//;
    
//    {age + 10}()
}

test()

七.自动闭包

是一种用来把实际参数传递给函数表达式打包的闭包,不接受任何实际参数,当其调用的是,返回内部表达式的值

好处:用普通表达式代替闭包的写法,语法糖的一种

func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
    if condition {
        print("lg_debug:\(message())")
    }
}

//只能传入String,编译器会将字符串变为闭包形式{return String}
debugOutPrint(true, "testString")
//v2必须传入的是函数/闭包表达式
func xxx(_ v1: Int, _ v2: () -> Int) -> Int {
    return v1 > 0 ? v1 + 1 : v2()
}

//v2必须传入的是值,然后编译器再生成闭包表达式{return String}
func xxx(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
    return v1 > 0 ? v1 : v2()
}

/*
 Swift中函数名相同,参数列表不同或者返回值类型不同的函数都可以构成重载
 因此这2个xxx函数就构成了函数的重载
 */

八.简单了解Swift多线程

Swift多线程默认还是调用的GCD(libdispatch)

1.Operation与DispatchWorkItem

执行一个任务

let operation = BlockOperation{
    print(#function)
}

operation.start()

let workItem = DispatchWorkItem{
    print(#function)
}

workItem.perform()

2.DispatchWorkItem可以cancel

class ViewController: UIViewController {

    lazy var workItem: DispatchWorkItem = {
        return DispatchWorkItem{
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
                guard self.workItem.isCancelled == false else {
                    return
                }
                print(#function)
            }

        }
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        DispatchQueue.global().async(execute: workItem)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        workItem.cancel()
    }

}

3.DispatchWorkItem实现Operation依赖的效果

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
        
    let workItem1 = DispatchWorkItem{
        print("1")
    }

    let workItem2 = DispatchWorkItem{
        print("2")
    }

    let workItem3 = DispatchWorkItem{
        print("3")
    }

    //类似于Operation中的addDependency
    workItem3.notify(queue: .global(), execute: workItem1)
    workItem1.notify(queue: .global(), execute: workItem2)
    DispatchQueue.global().async(execute: workItem3)
    //3,1,2
}

4.Operation依赖是怎么形成的

/*
 依赖形成的原因:Operation里有一个数组__dependencies来保存依赖的任务,
 当所有的依赖任务全部执行完成后(isReady)才能执行自己
 
 当前任务执行的先决条件:是否处于isReady的状态或者依赖数组执行完毕
 */
internal func _addDependency(_ op: Operation) {
    withExtendedLifetime(self) {
        withExtendedLifetime(op) {
            var up: Operation?
            
            _lock()
            if __dependencies.first(where: { $0 === op }) == nil {
                __dependencies.append(op)
                up = op
            }
            _unlock()
            
            if let upwards = up {
                upwards._lock()
                _lock()
                let upIsFinished = upwards._state == __NSOperationState.finished
                if !upIsFinished && !_isCancelled {
                    assert(_unfinishedDependencyCount >= 0)
                    
                    //增加未完成的依赖个数, += 1
                    _incrementUnfinishedDependencyCount()
                    
                    upwards._addParent(self)
                }
                _unlock()
                upwards._unlock()
            }
            
            //当前未完成的依赖数组个数为0时(isReady),执行当前任务
            Operation.observeValue(forKeyPath: _NSOperationIsReady, ofObject: self)
        }
    }
}

5.Operation任务放入队列中是怎么调度的

internal func _addOperations(_ ops: [Operation], barrier: Bool = false) {
 
    //前面代码依据任务生命周期的状态形成链表,会依次调度链表的任务
    ...
    
    if !barrier {
        _schedule()
    }
}

internal func _schedule() {
 
    ...
    
    /*
     本质上还是通过GCD来进行任务的调度
     */
    let queue: DispatchQueue
    if __mainQ {
        queue = DispatchQueue.main
    } else {
        queue = __dispatch_queue ?? _synthesizeBackingQueue()
    }
    
    if let schedule = operation.__schedule {
        if operation is _BarrierOperation {
            queue.async(flags: .barrier, execute: {
                schedule.perform()
            })
        } else {
            //有且只有一条线程~
            queue.async(execute: schedule)
        }
    }
    
    ...
}

6.了解Operation

func aboutOperation(){
        /*
         1.认识Operation
         Operation是一个抽象类,对GCD进行封装,并不具备封装操作的能力。
         必须使用它的子类:BlockOperation、自定义子类继承Operation、NSInvocationOperation(Swift中不能使用,OC中使用)
         */
        
        let opBlock = BlockOperation{
            print("BlockOperation")
        }
        
        //注意这里任务执行方式为串行
        opBlock.start()
        
        /*
         2.使用OperationQueue
         */
        
        //默认是并发的,如果设置maxConcurrentOperationCount为1,则为串行队列
        let queue = OperationQueue()
        //最大并发数
        queue.maxConcurrentOperationCount = 2
        
        /*
         3.设置任务队列优先级
         */
        let op1 = BlockOperation{
            print("1")
        }
        
        let op2 = BlockOperation{
            print("2")
        }
        
        let op3 = BlockOperation{
            print("3")
        }
        
        //当队列并发数<任务个数,先执行队列优先级高的任务
        //当队列并发数>任务个数,所有任务一起执行
        op1.queuePriority = .low
        op2.queuePriority = .high
        op3.queuePriority = .normal
        
        //waitUntilFinished如果为true会阻塞线程,直到ops中所有任务执行完成后,后面的任务才能执行
        queue.addOperations([op1, op2, op3], waitUntilFinished: false)
        
        /*
         4.Operation依赖关系
         */
        let opA = BlockOperation{
            print("A")
        }
        let opB = BlockOperation{
            print("B")
        }
        let opC = BlockOperation{
            print("C")
        }
        let opD = BlockOperation{
            print("D")
        }
        let opE = BlockOperation{
            print("E")
        }
        
        /*
         C依赖于A、B
         E依赖于C、D
         
         A和B执行完才能执行C
         C和D执行完才能执行E
         
         E永远最后执行
         */
        opC.addDependency(opA)
        opC.addDependency(opB)
        opE.addDependency(opC)
        opE.addDependency(opD)
        
        let queue1 = OperationQueue()
        queue1.maxConcurrentOperationCount = 5
        queue1.addOperations([opA,opB,opC,opD,opE], waitUntilFinished: false)
    }

7.Swift中使用dispatch_once

在Swift3后就废弃了dispatch_once函数,因为Swfit创建单例本来就是线程安全的

extension DispatchQueue {
    
    private static var _tokens = [String]()
    
    static func once(token: String, block: () -> Void) {
        //确保线程安全
        objc_sync_enter(self)
        defer{
            objc_sync_exit(self)
        }
        if _tokens.contains(token) {
            return
        }
        _tokens.append(token)
        block()
    }
}

let uuid = UUID().uuidString
DispatchQueue.once(token: uuid) {
    //doSomething
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 前言 本篇文章主要讲解Swift中又一个相当重要的知识点 👉 闭包,首先会介绍闭包的概念,包含与OC中Block的...
    深圳_你要的昵称阅读 424评论 0 8
  • 一、闭包 1.1、闭包表达式(Closure Expression)在 Swift 里面可以通过函数 func 定...
    IIronMan阅读 946评论 0 2
  • 什么是闭包 维基百科中的解释:在计算机科学中,闭包(Closure),又称词法闭包(Lexical Closure...
    帅驼驼阅读 357评论 0 3
  • @[TOC](Swift (一)--闭包) 1. 闭包简介 什么是闭包 闭包就是能够读取其他函数内部变量的函数,可...
    孔雨露阅读 639评论 0 2
  • 闭包 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C ...
    xiaofu666阅读 2,849评论 0 1