11

Tôi muốn quấn một API async mà trông như thế này:cách quấn một phương pháp không đồng bộ mà phải mất một khối và biến nó đồng bộ trong quan c

[someObject completeTaskWithCompletionHandler:^(NSString *result) { 

}]; 

thành một phương pháp đồng bộ mà tôi có thể gọi như

này
NSString *result = [someObject completeTaskSynchronously]; 

Làm cách nào để thực hiện việc này? Tôi đã làm một số đọc doc và tìm kiếm google, và cố gắng sử dụng "dispatch_semaphore" làm cố gắng để đạt được nó như vậy:

-(NSString *) completeTaskSynchronously { 
    __block NSString *returnResult; 
    self.semaphore = dispatch_semaphore_create(0); 
    [self completeTaskWithCompletionHandler:^(NSString *result) { 
     resultResult = result; 
     dispatch_semaphore_signal(self.semaphore); 
    }]; 

    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); 
    return resultResult; 
} 

Nhưng điều này không có vẻ làm việc, nó về cơ bản chỉ dừng ở dispatch_semaphore_wait. Thực hiện không bao giờ đạt đến khối bên trong mà làm _signal. Bất cứ ai cũng có mã ví dụ về cách làm điều này? Tôi nghi ngờ rằng các khối đã được trên một chủ đề khác nhau các chủ đề chính? Ngoài ra, giả sử tôi không có quyền truy cập vào mã nguồn đằng sau phương thức async. Cảm ơn!

+4

Nếu trình xử lý hoàn thành được thực hiện trên cùng một luồng gọi dispatch_semaphore_wait, bạn thực sự bế tắc chuỗi vì khối hoàn thành không thể thực thi cho đến khi thoát khỏi chuỗi chờ. Bạn đang cố gắng làm điều này trên chủ đề chính? Nó là tốt hơn để không chặn các chủ đề chính trong thời gian dài bởi vì nó phải liên tục gửi tin nhắn. – yurish

+0

Nếu, như nghi ngờ bởi @yurish, bạn xử lý đã được xếp hàng đợi vào luồng công văn chính, bạn không được chờ đợi. Bạn phải xây dựng dòng mã của bạn như một máy trạng thái và làm bất cứ điều gì cần phải được thực hiện với kết quả trong trình xử lý hoàn thành. –

+2

Không có cách chung để làm điều này. Như những người khác đã nói, nếu một phần nào đó của tác vụ không đồng bộ hoạt động bằng cách đặt các sự kiện trên vòng lặp chạy, bạn sẽ luôn luôn bế tắc. Bạn đang thực sự * cố gắng đạt được điều gì? Có thể có một cách khác để cấu trúc mã của bạn. – JeremyP

Trả lời

-2

Bạn có thể thử sử dụng NSOperations để làm việc này không đồng bộ.

8

dispatch_semaphore_wait chặn hàng đợi chính trong ví dụ của bạn. Bạn có thể gửi công việc async để một hàng đợi khác nhau:

__block NSString *returnResult; 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL); 
dispatch_async(queue,^{ 
    result = [someObject completeTaskSynchronously]; 
}); 

Hoặc sử dụng một số hệ thống khác, như NSRunLoop:

__block finished = NO; 
    [self completeTaskWithCompletionHandler:^(NSString *result) { 
     resultResult = result; 
     finished = YES; 
    }]; 
    while (!finished) { 
     // wait 1 second for the task to finish (you are wasting time waiting here) 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; 
    } 
6

Sử dụng một NSRunLoop là dễ nhất để làm ở đây.

__block NSString* result = nil; 
[self completeTaskWithCompletionHandler:^(NSString *resultstring) { 
    result = resultstring; 
}]; 
while (!result) { 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
} 
+0

Có, tôi nghĩ rằng điều này hoạt động tốt. Tôi cũng tìm thấy một lớp MTTestSemaphore trên github mà đóng gói này và làm cho nó giống như semaphore hơn. Cảm ơn. – kawingkelvin

+0

Tôi tự hỏi điều này tương tác với sự kiện xử lý sự kiện bình thường như thế nào? Những thứ như giao dịch Hoạt ảnh chính vv – yurish

+3

Điều này không thể hoạt động tốt. Vòng lặp chạy yêu cầu các sự kiện được xử lý để trả về. Nếu không có sự kiện, nó sẽ chỉ trở lại trong "tương lai xa", đó là * không bao giờ *. Hơn nữa, việc truy cập vào con trỏ 'result' không phải là luồng an toàn. Nó không đảm bảo rằng một con trỏ không phải là số không và hoàn toàn khởi tạo. Và (có thể) một trình biên dịch có thể được phép tối ưu hóa truy cập vào vị trí bộ nhớ và giữ con trỏ trong sổ đăng ký không bao giờ được cập nhật. – CouchDeveloper

1

Tôi nghĩ giải pháp tốt hơn sẽ là NSRunLoop như được nêu bên dưới. Nó đơn giản và làm việc tốt.

- (NSString *)getValue { 

    __block BOOL _completed = NO; 
    __block NSString *mValue = nil; 


    [self doSomethingWithCompletionHandler:^(id __nullable value, NSError * __nullable error) { 
     mValue = value; 
     _completed = YES; 
    }]; 

    while (!_completed) { 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    } 

    return mValue; 
} 
Các vấn đề liên quan