dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
该函数的作用是保证block在程序的生命周期范围内只执行一次。
参数
predicate:该参数是为了表示该block是否执行过。 0表示未执行过 ,1表示执行过 block:该block就是只执行一次的任务。
通过下面的例1和例2,我们可以知道每次执行block前先对 onceToken 的值进行检查,0表示第一次执行,执行block后将onceToken 置为-1.如果onceToken 为-1,表示非第一执行,则不执行block.
例1:
// dispatch_once_t 必须是全局变量或static变量(保证dispatch_once_t只有一份实例)
static dispatch_once_t onceToken;
NSLog(@"AonceToken = %ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"BonceToken = %ld", onceToken);
});
NSLog(@"ConceToken = %ld", onceToken);
运行结果:
2016-10-26 05:55:21.727 Test1[12244:878037] AonceToken = 0
2016-10-26 05:55:21.727 Test1[12244:878037] BonceToken = 140734777330000
2016-10-26 05:55:21.727 Test1[12244:878037] ConceToken = -1
例2:
static dispatch_once_t onceToken;
NSLog(@"AonceToken = %ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"BonceToken = %ld", onceToken);
});
dispatch_once(&onceToken, ^{
NSLog(@"DonceToken = %ld", onceToken);
});
NSLog(@"ConceToken = %ld", onceToken);
运行结果:
2016-10-26 05:59:54.945 Test1[12277:880650] AonceToken = 0
2016-10-26 05:59:54.945 Test1[12277:880650] BonceToken = 140734583380288
2016-10-26 05:59:54.945 Test1[12277:880650] ConceToken = -1
应用:如果想要某个行为在整个生命周期中只能被执行一次。最常用到的地方是例3 单例
例3: 单例
#import <Foundation/Foundation.h>
@interface TestObject : NSObject
+ (TestObject *)shareObject;
@end
@implementation TestObject
+ (TestObject *)shareObject {
static TestObject *share = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"进入了哈哈哈");
if (!share) {
share = [[TestObject alloc]init];
}
});
return share;
}
@end
调用:
for (int i=0; i<2; i++) {
NSLog(@"第%d次--%@",i,[TestObject shareObject]);
}
结果:
2016-10-26 06:12:47.859 Test1[12436:886784] 进入了哈哈哈
2016-10-26 06:12:47.860 Test1[12436:886784] 第0次--<TestObject:0x7f8263d4b860>
2016-10-26 06:12:47.861 Test1[12436:886784] 第1次--<TestObject:0x7f8263d4b860>
可以发现多次“创建”对象的内存是一样的。也只有第一次创建时真正的创建。
原理:
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
进入该方法,我们可以去看一下它的实现
void _dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) {
if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
dispatch_once(predicate, block); //a
}
}
如果predicate != -1 执行if里面的语句。因为没法看到a语句具体的实现,我们只能知道 a语句会执行block,同时将predicate = -1。但具体如何实现不知道。
理解1.1:#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))
__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间浪费.__builtin_expect仅仅是告诉编译器优化,并没有改变其对真值的判断。
比如在内核代码中,经常会遇到 #define likely(x) __builtin_expect(!!(x), 1)
__builtin_expect()告诉编译器,x可能是1的概率很大,编译器可以假设x是1的情况去对接下来的操作进行优化,以减少指令跳转带来的性能降低。但具体是不是1还要进行正式的判断,毕竟你x不一定是1.
理解1.2 ~0l
~0l 表示一个长整形(long)的0按位取反。 0l 写成16进制 0x00000000 ~0l 就是 0xFFFFFFFF 即-1
网友评论