Câu trả lời này phải được niêm yết ở đây:: - Trong trường hợp của tôi ngay trước khi lên kế hoạch tiếp theo gọi trì hoãn cancel dispatch_after() method?, nhưng đó là đóng cửa như là một bản sao (nó thực sự không phải là). Dù sao, đây là một nơi mà google trả về cho "dispatch_after cancel", vì vậy ...
Câu hỏi này khá cơ bản và tôi chắc chắn có những người muốn một giải pháp thực sự chung chung mà không cần đến các nền tảng cụ thể như runloop timers, boolean instance và/hoặc magic block nặng. GCD có thể được sử dụng như một thư viện C thông thường và có thể không có thứ gì như một bộ đếm thời gian cả.
May mắn thay, có cách để hủy bất kỳ khối công văn nào trong bất kỳ chương trình đời nào.
- Chúng tôi phải đính kèm xử lý động vào từng khối mà chúng tôi chuyển đến dispatch_after (hoặc dispatch_async, không thực sự quan trọng).
- Tay cầm này phải tồn tại cho đến khi khối thực sự được kích hoạt.
- Quản lý bộ nhớ cho xử lý này không rõ ràng - nếu khối giải phóng xử lý, sau đó chúng tôi có thể dereference lơ lửng con trỏ sau này, nhưng nếu chúng tôi giải phóng nó, khối có thể làm điều đó sau.
- Vì vậy, chúng tôi phải chuyển quyền sở hữu theo yêu cầu.
- Có 2 khối - một khối là khối điều khiển sẽ kích hoạt và thứ hai là số tiền trọng tải có thể bị hủy.
struct async_handle {
char didFire; // control block did fire
char shouldCall; // control block should call payload
char shouldFree; // control block is owner of this handle
};
static struct async_handle *
dispatch_after_h(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t payload)
{
struct async_handle *handle = malloc(sizeof(*handle));
handle->didFire = 0;
handle->shouldCall = 1; // initially, payload should be called
handle->shouldFree = 0; // and handles belong to owner
payload = Block_copy(payload);
dispatch_after(when, queue, ^{
// this is a control block
printf("[%p] (control block) call=%d, free=%d\n",
handle, handle->shouldCall, handle->shouldFree);
handle->didFire = 1;
if (handle->shouldCall) payload();
if (handle->shouldFree) free(handle);
Block_release(payload);
});
return handle; // to owner
}
void
dispatch_cancel_h(struct async_handle *handle)
{
if (handle->didFire) {
printf("[%p] (owner) too late, freeing myself\n", handle);
free(handle);
}
else {
printf("[%p] (owner) set call=0, free=1\n", handle);
handle->shouldCall = 0;
handle->shouldFree = 1; // control block is owner now
}
}
Vậy là xong.
Điểm chính là "chủ sở hữu" nên thu thập tay cầm cho đến khi nó không cần chúng nữa. dispatch_cancel_h() hoạt động như một destructor [có khả năng trì hoãn] cho một xử lý.
C chủ sở hữu dụ:
size_t n = 100;
struct after_handle *handles[n];
for (size_t i = 0; i < n; i++)
handles[i] = dispatch_after_h(when, queue, ^{
printf("working\n");
sleep(1);
});
...
// cancel blocks when lifetime is over!
for (size_t i = 0; i < n; i++) {
dispatch_cancel_h(handles[i]);
handles[i] = NULL; // not our responsibility now
}
Objective-C ARC dụ:
- (id)init
{
self = [super init];
if (self) {
queue = dispatch_queue_create("...", DISPATCH_QUEUE_SERIAL);
handles = [[NSMutableArray alloc] init];
}
return self;
}
- (void)submitBlocks
{
for (int i = 0; i < 100; i++) {
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (random() % 10) * NSEC_PER_SEC);
__unsafe_unretained id this = self; // prevent retain cycles
struct async_handle *handle = dispatch_after_h(when, queue, ^{
printf("working (%d)\n", [this someIntValue]);
sleep(1);
});
[handles addObject:[NSValue valueWithPointer:handle]];
}
}
- (void)cancelAnyBlock
{
NSUInteger i = random() % [handles count];
dispatch_cancel_h([handles[i] pointerValue]);
[handles removeObjectAtIndex:i];
}
- (void)dealloc
{
for (NSValue *value in handles) {
struct async_handle *handle = [value pointerValue];
dispatch_cancel_h(handle);
}
// now control blocks will never call payload that
// dereferences now-dangling self/this.
}
Ghi chú:
- dispatch_after() ban đầu được giữ lại hàng đợi, vì vậy nó sẽ tồn tại cho đến khi tất cả các khối điều khiển được thực hiện.
- async_handles được giải phóng nếu tải trọng bị hủy (hoặc thời gian tồn tại của chủ sở hữu đã kết thúc) VÀ khối điều khiển đã được thực thi.
- Chi phí bộ nhớ động của async_handle hoàn toàn nhỏ so với cấu trúc bên trong dispatch_after() và dispatch_queue_t, giữ lại một mảng khối thực sự được gửi và khử chúng khi thích hợp.
- Bạn có thể nhận thấy rằng shouldCall và shouldFree thực sự là cùng một cờ đảo ngược. Nhưng trường hợp chủ sở hữu của bạn có thể vượt qua quyền sở hữu và thậm chí - [dealloc] chính nó mà không thực sự hủy bỏ khối tải trọng, nếu những điều này không phụ thuộc vào "tự" hoặc dữ liệu liên quan đến chủ sở hữu khác. Điều này có thể được thực hiện với đối số shouldCallAnyway bổ sung để dispatch_cancel_h().
- Lưu ý cảnh báo: giải pháp này cũng thiếu đồng bộ cờ didXYZ và có thể gây ra một cuộc chạy đua giữa khối điều khiển và quy trình hủy. Sử dụng OSAtomicOr32Barrier() & đồng để đồng bộ hóa.
Bạn là đúng, đó là giải pháp tốt hơn! –
Cảm ơn rất nhiều! Quá dễ! Tôi nên nghĩ về điều đó bản thân mình –