iOS Block異步接口轉(zhuǎn)同步接口的方法

異步Block接口轉(zhuǎn)為同步接口

1.異步Block接口

通常在遇到本地IO操作,網(wǎng)絡(luò)請求等情況,我們習(xí)慣于使用Block作為回調(diào),獲取操作的結(jié)果,比如:

doSomething({
    // success
},{
    // failuer
})

但是當(dāng)業(yè)務(wù)復(fù)雜,需要處理的異步任務(wù)比較多時(shí),就會出現(xiàn)回調(diào)地獄的問題:

doSomething({
    doSomething({
        doSomething({
            doSomething({
                doSomething({
                    // =======================
                })
            })
        })
    })
})

基于這個(gè)問題,出現(xiàn)了一些解決方案,使用比較多的,就是Promise,將這種"粽子"調(diào)用改成"寬面"的調(diào)用:

doSomething()
.then({
    // =======================
})
.then({
    // =======================
})
.thern({
    // =======================
})
.thern({
    // =======================
})
.thern({
    // =======================
})

但是這種代碼還是不夠優(yōu)雅,于是又有了async/await的方案:

await doSomething();
await doSomething();
await doSomething();
await doSomething();
await doSomething();

這個(gè)就是我們理想中的代碼。

PS:以上代碼為偽碼

2.iOS 將 block 轉(zhuǎn)為同步

理想很豐滿,現(xiàn)實(shí)很骨感。Objective-C 沒有提供 async/await 方案。如果自己擼一套,由于沒有編譯器支持,自己添加語法糖的話,會把代碼搞的更丑,因此我嘗試直接將 Block 轉(zhuǎn)為同步。

原理很簡單,就是在 block 回調(diào)時(shí),做一次線程同步:

改造前:

- (void)sendMessage:(NSString *)message callback:(MissionCallback)callback {
    float random = 1 + (arc4random()%100)/30.0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(random * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString *tag = [NSString stringWithFormat:@"%@ - random:%f", message, random];
        callback(tag);
    });
}

改造后

- (NSString *)sendMessageSemaphore:(NSString *)message {
    __block NSString *resultMessage;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self sendMessage:message callback:^(NSString * _Nonnull result) {
        resultMessage = result;
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return resultMessage;
}

代碼很簡單,最終的效果也差強(qiáng)人意。實(shí)測修改后,該接口同時(shí)支持63方并發(fā)訪問,再多就會卡死。

至于為什么是63這個(gè)數(shù)字,是因?yàn)間cd最大線程數(shù)是63。當(dāng)使用gcd創(chuàng)建了64個(gè)線程并發(fā)時(shí),再申請線程資源會被阻塞,直到有其他線程資源被釋放。而本例中由于進(jìn)行線程同步需要占用當(dāng)前線程,因此當(dāng)有64個(gè)任務(wù)并發(fā)時(shí),就無法再創(chuàng)建callback的線程,導(dǎo)致所有任務(wù)卡死。

附 xnu 地址:https://github.com/apple/darwin-xnu

3.Block 轉(zhuǎn)同步的其他姿勢

試驗(yàn)中,分別用信號量、條件鎖、互斥鎖、自旋鎖寫了demo,最終的實(shí)測效果和上面一樣。性能上,dispatch_semaphore 和 pthread_mutex 性能比較好;OSSpinLock 不再安全已經(jīng)廢棄;NSCondition/NSConditionLock 更加靈活一些,Objective-C的接口對一些不熟悉c的程序員更友好。

以下是幾種不同的方法的代碼實(shí)現(xiàn):

// Block
- (void)sendMessage:(NSString *)message callback:(MissionCallback)callback {
    float random = 1 + (arc4random()%100)/30.0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(random * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString *tag = [NSString stringWithFormat:@"%@ - random:%f", message, random];
        callback(tag);
    });
}

// 信號量
- (NSString *)sendMessageSemaphore:(NSString *)message {
    __block NSString *resultMessage;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self sendMessage:message callback:^(NSString * _Nonnull result) {
        resultMessage = result;
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    return resultMessage;
}

// 條件鎖
- (NSString *)sendMessageCondition:(NSString *)message {
    __block NSString *resultMessage;
    NSCondition *condition = [[NSCondition alloc] init];
    [condition lock];
    [self sendMessage:message callback:^(NSString *result) {
        resultMessage = result;
        [condition signal];
    }];
    [condition wait];
    [condition unlock];
    return resultMessage;
}

- (NSString *)sendMessageConditionLock:(NSString *)message {
    __block NSString *resultMessage;
    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
    [self sendMessage:message callback:^(NSString *result) {
        resultMessage = result;
        [lock lock];
        [lock unlockWithCondition:0];
    }];
    [lock lockWhenCondition:0];
    [lock unlock];
    return resultMessage;
}

// 互斥鎖
- (NSString *)sendMessageMutex:(NSString *)message {
    __block NSString *resultMessage;
    pthread_mutex_t pMutex = PTHREAD_MUTEX_INITIALIZER;
    __block pthread_cond_t pCond = PTHREAD_COND_INITIALIZER;
    
    pthread_mutex_lock(&pMutex);
    [self sendMessage:message callback:^(NSString *result) {
        resultMessage = result;
        pthread_cond_signal(&pCond);
    }];
    pthread_cond_wait(&pCond, &pMutex);
    pthread_mutex_destroy(&pMutex);
    return resultMessage;
}

// 自旋鎖
- (NSString *)sendMessageSpin:(NSString *)message {
    __block NSString *resultMessage;
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    
    OSSpinLockLock(&oslock);
    [self sendMessage:message callback:^(NSString *result) {
        resultMessage = result;
        OSSpinLockUnlock(&oslock);
    }];
    OSSpinLockLock(&oslock);
    return resultMessage;
}

本文Demo代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,681評論 1 32
  • layout: posttitle: 《Java并發(fā)編程的藝術(shù)》筆記categories: Javaexcerpt...
    xiaogmail閱讀 6,024評論 1 19
  • 翻譯:Synchronization 同步 應(yīng)用程序中存在多個(gè)線程會導(dǎo)致潛在的問題,這些問題可能會導(dǎo)致從多個(gè)執(zhí)行線...
    AlexCorleone閱讀 2,734評論 0 4
  • 引用自多線程編程指南應(yīng)用程序里面多個(gè)線程的存在引發(fā)了多個(gè)執(zhí)行線程安全訪問資源的潛在問題。兩個(gè)線程同時(shí)修改同一資源有...
    Mitchell閱讀 2,123評論 1 7
  • 其實(shí),不光是在職場上生活中,在戀愛中也有自己忌憚害怕的人。九數(shù)也是如此,所謂一物降一物,一看到他,原本乖張的自己,...
    讀書人涯涯閱讀 391評論 0 2

友情鏈接更多精彩內(nèi)容