Touch ID是iOS8上新公开的API,关于详细介绍和用法可以看CocoaChina的这两篇文章:上 和 下,在此篇文章中不在赘述。
我在app中需要的效果是如果touch id验证通过,则页面push到下一个viewController,否则本视图的数字密码输入框becomeFirstResponder。研究过touch id的人应该知道,这段代码大概会这么实现:
1 LAContext *context = [[LAContext alloc] init]; 2 NSError *error; 3 4 BOOL canUse = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; 5 if (canUse) { 6 [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics 7 localizedReason:@"使用touch ID来打开" 8 reply:^(BOOL success, NSError *authenticationError) { 9 if (success) { 10 //push视图 11 } else { 12 //弹出输入框 13 } 14 }]; 15 }
我一开始确实也是这么写的,结果发现,验证完成后,无论是否成功,界面都会卡了5秒以上才会进行下一步。这显然不科学啊,然后仔细看了一下这个方法的说明,在网上搜索一番,最后在stackoverflow的帮助下才弄清。一晚上时间的成果。。。。
我们先看一下- (void)evaluatePolicy:(LAPolicy)policy localizedReason:(NSString *)localizedReason reply:(void (^)(BOOL success, NSError *error))reply这个方法对最后一个参数reply这个block的描述:
reply
Reply block that is executed when policy evaluation finishes. This block is evaluated on a private queue internal to the framework in an unspecified threading context. You must not call canEvaluatePolicy:error: in this block, because doing so could lead to deadlock.
注意第二句话,大概意思是(英语不是很好,轻喷):这个block会在framework内部的一个私有队列中进行判断,而这个framework更是在一个不确定的线程中。。。说简单点就是,这个block就不在主线程中执行啊有木有!刷新界面的操作必须要放在主线程中啊有木有!意识到这点再回过头来看网上的那些讲解包括官方Demo,都只是在reply中NSLog啊、println啊、给变量赋值啊,让我抄的时候完全忽视线程这个东西。。。。
接下来就容易解决了,要不就定义一个是否验证成功的BOOL flag,在reply里判断赋值,然后紧跟着在方法外判断flag来进行下一步;要不就直接点,把reply里的操作放到主线程里,形如
1 [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics 2 localizedReason:@"使用touch ID来打开" 3 reply:^(BOOL success, NSError *authenticationError) { 4 dispatch_async(dispatch_get_main_queue(), ^{ 5 if (success) { 6 //push 7 } else { 8 //弹出输入框 9 } 10 }); 11 }];