Tôi cần lưu ý đầu tiên rằng đây không phải là điều tốt để làm trong bất kỳ tình huống nào ngoài thử nghiệm; thậm chí sau đó, tiến hành cẩn thận - AliSoftware cung cấp một số chi tiết và mã ví dụ trong phần bình luận bên dưới. Xem thêm các câu trả lời thú vị tại Can I declare a dispatch_once_t
predicate as a member variable instead of static?, bao gồm một số thông tin quan trọng from the horse's mouth.
dispatch_once_t
là typedef
d long
. Giá trị sai của nó là 0. Nếu bạn đặt lại cờ đó thành 0, dispatch_once()
sẽ chạy lại. Vấn đề của bạn là "chỉ" làm thế nào để thay đổi giá trị của một biến tĩnh từ một đơn vị biên dịch khác. Đối với điều này, tôi nghĩ rằng bạn cần một cái móc thử nghiệm debug/đơn vị, như vậy:
MakeWhoopie.h
#import <Foundation/Foundation.h>
void makeWhoopie(void);
#ifdef DEBUG
void resetDispatchOnce(void);
#endif
MakeWhoopie.m
#include "MakeWhoopie.h"
static dispatch_once_t * once_token_debug;
void makeWhoopie(void)
{
static dispatch_once_t once_token;
once_token_debug = &once_token; // Store address of once_token
// to access it in debug function.
dispatch_once(&once_token, ^{
NSLog(@"That's what you get, folks.");
});
NSLog(@"Making whoopie.");
}
#ifdef DEBUG
void resetDispatchOnce(void)
{
*once_token_debug = 0;
}
#endif
(Bạn cũng có thể di chuyển once_token
lên đến nộp và thay đổi trực tiếp.)
Hãy thử cách này:
#import <Foundation/Foundation.h>
#import "MakeWhoopie.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
makeWhoopie();
makeWhoopie();
resetDispatchOnce();
makeWhoopie();
}
return 0;
}
Kết quả trong:
2012-06-07 18: 45: 28,134 ResetDispatchOnce [8628: 403] Đó là những gì bạn nhận được, folks.
2012-06-07 18: 45: 28.163 ResetDispatchOnce [8628: 403] Làm whoopie.
2012-06-07 18: 45: 28.164 ResetDispatchOnce [8628: 403] Làm whoopie.
2012-06-07 18: 45: 28.165 ResetDispatchOnce [8628: 403] Đó là những gì bạn nhận được, mọi người.
2012-06-07 18: 45: 28.165 ResetDispatchOnce [8628: 403] Làm whoopie.
Nguồn
2012-06-07 18:51:32
Điểm của 'dispatch_once' là an toàn chỉ. Vấn đề bằng cách thiết lập '* once_token_debug = 0' theo cách này là nó không an toàn cho thread trong trường hợp một luồng khác sử dụng' dispatch_once (& onceToken,…) 'trong khi bạn tự đặt' onceToken'. Làm thế nào chúng ta có thể ngăn chặn vấn đề như vậy? – AliSoftware
@AliSoftware: Kịch bản trong câu hỏi là kiểm tra đơn vị và đó là cách sử dụng _only_ tôi đề xuất cho quy trình này. Mã thông báo đang được thiết lập lại các kiểm tra _between_, để tạo một slate sạch giống như sẽ được tạo ra giữa các lần chạy của chương trình lớn hơn. Luồng không phải là vấn đề vì chương trình không chạy giữa các lần kiểm tra. –
Tôi hiểu điều này, nhưng tất cả mọi người phải nhận thức được điều đó và không bị cám dỗ để sử dụng điều này một cách không an toàn ở đâu đó.Và có thể có một số trường hợp ngay cả trong Bài kiểm tra đơn vị rằng đây không phải là an toàn luồng, đặc biệt nếu một mã xấu kiểm tra và thực hiện các hành động không đồng bộ tiếp tục chạy ngay cả sau khi kiểm tra kết thúc (như một cuộc gọi đến 'dispatch_after (10s,^{/ * sthg sử dụng sharedInstance * /} 'nếu thử nghiệm đạt đến timeout của nó nó sẽ thất bại và bị dừng lại và tiếp cận' tearDown' nhưng khối sử dụng sharedInstance sẽ vẫn được gửi đi trong tương lai gần… sau tearDown – AliSoftware