我一直在用一个新的api为一些sensorTag装箱。我需要等待连接解决,否则我的代码片会在第一次运行时失败。我有以下代码段:
- (void)viewDidLoad {
[super viewDidLoad];
self.sensorGyro = [MSBSensorUV createObjectGyro];
if(self.sensorGyro){
while (!self.sensorGyro.client.isDeviceConnected) {
NSLog(@"Awaiting connection");
}//Check that connection has been made
NSLog(@"connection UP");
[self.sensorGyro.client.sensorManager startGyroUpdatesToQueue:nil errorRef:nil withHandler:^(MSBSensorGyroData *GyroData, NSError *error) {
NSLog(@"Starting updates");
self.currentGyroLabel.text = [NSString stringWithFormat:@"%lu", GyroData.GyroIndexLevel];
}];
最初我没有while循环。我用了2-3秒的dispatch_after电话。但我觉得等待一段固定的时间并不好,最好是在连接时继续。但是,当我使用while循环时,我的应用程序只是停止并停止工作。没有崩溃,只是停下来。任何人都可以告诉我为什么会这样。因为我已经验证连接只花了2秒钟,所以我没有看到做while循环的问题?还有,有更好的方法来等待"用于完成/干燥的东西。
提前致谢
答案 0 :(得分:2)
MSBSensorUV
类正在启动一些异步连接过程。
如果您要循环等待状态更改,您可以将其发送到后台线程(如Luke加密建议的那样)。这可以防止应用程序阻塞主线程。
问题是你仍然有一个线程被捆绑在一个浪费的练习中不断轮询,看看isDeviceConnected
是否真实。这是一个程序相当于一个汽车后座的孩子不断询问"我们在那里吗?"
更好的是,您可以采用事件驱动模式,例如使用Matteo建议的KVO。这种方式而不是此代码不断轮询,当isDeviceConnected
属性更改时,您将收到通知。
这样做效率更高,如果您遇到一个没有提供完成块的类,但确实异步更改某些属性,这是一个很好的方法。
最好,因为MSBSensorUV
是您自己的类,您可以重构它以提供连接完整处理程序块。这样,您就可以避免上述方法引入的复杂情况。
离线,您分享了MSBSensorUV
实施的一些内容:
+ (MSBSensorUV *)createObjectUV{
static MSBSensorUV *UVObject;
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
UVObject = [[MSBSensorUV alloc]init];
});
return UVObject;
}
- (MSBSensorUV *)init{
self = [super init];
if(self){
[MSBClientManager sharedManager].delegate = self;
NSArray *clients = [[MSBClientManager sharedManager] attachedClients];
self.client = [clients firstObject];
if(self.client == nil){
//no bands attached
return nil;
}
[[MSBClientManager sharedManager] connectClient:self.client];
}
return self;
}
首先,createObjectUV
正在创建一个单身,因此您应该将其重命名为sharedSensor
(使用前缀shared
,以明确您正在处理单身人士):
+ (instancetype)sharedSensor {
static MSBSensorUV *uvObject;
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
uvObject = [[MSBSensorUV alloc] init];
});
return uvObject;
}
其次,init
不应该开始连接。
- (instancetype)init {
self = [super init];
if (self) {
[MSBClientManager sharedManager].delegate = self;
NSArray *clients = [[MSBClientManager sharedManager] attachedClients];
self.client = [clients firstObject];
if(self.client == nil){
//no bands attached
return nil;
}
// [[MSBClientManager sharedManager] connectClient:self.client];
}
return self;
}
坦率地说,在单身人士的背景下,如果没有附加乐队,返回nil
的概念可能没有意义(因为一旦你将它设置为nil
在终止应用程序之前,你永远不能再次访问这个单例。所以你可能也想把这个逻辑从init
中拉出来,但我会让你自己解决这个问题。
第三,您将为连接完成处理程序声明一个属性:
@property (nonatomic, copy) void (^connectionCompletionHandler)(BOOL success, NSError *);
第四,您将创建一个connectionWithCompletionHandler
方法,例如:
- (void)connectWithCompletionHandler:(void (^)(BOOL success, NSError *error))block
{
self.connectionCompletionHandler = block;
[[MSBClientManager sharedManager] connectClient:self.client];
}
您的连接委托方法将调用此完成处理程序,例如clientDidConnect
会致电:
if (self.connectionCompletionHandler) {
self.connectionCompletionHandler(TRUE, nil);
self.connectionCompletionHandler = nil;
}
didFailToConnectWithError
会报告失败:
if (self.connectionCompletionHandler) {
self.connectionCompletionHandler(FALSE, error);
self.connectionCompletionHandler = nil;
}
最后,回到原始代码示例,它看起来像:
- (void)viewDidLoad {
[super viewDidLoad];
self.sensorGyro = [MSBSensorUV sharedSensor];
[self.sensorGyro connectWithCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"connection UP");
[self.sensorGyro.client.sensorManager startGyroUpdatesToQueue:nil errorRef:nil withHandler:^(MSBSensorGyroData *GyroData, NSError *error) {
NSLog(@"Starting updates");
self.currentGyroLabel.text = [NSString stringWithFormat:@"%lu", GyroData.GyroIndexLevel];
}];
} else {
NSLog(@"Error connecting: %@", error);
}
}];
}
答案 1 :(得分:1)
您可以使用 KVO ,然后观察变量:
self.sensorGyro.client.isDeviceConnected
当变量时,观察者会收到通知。
所以你最初应该显示一个微调器(加载),并设置:
self.view.userInteractionEnabled = NO;
然后,当您收到通知时,您应该删除微调器并将用户交互设置为启用(YES)。
添加观察者很容易:
[self.sensorGyro.client addObserver:self forKeyPath:@"isDeviceConnected" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
您必须添加此方法,该方法将在变量更改时调用:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"isDeviceConnected"]) {
// Your code here
}
}
最后记得删除方法-dealloc
中的观察者:
- (void)dealloc
{
[self.self.sensorGyro.client removeObserver:self ....];
}
重要:
如果使用访问者方法设置属性isDeviceConnected
,则会自动发送通知,因此:self.isDeviceConnected
。
有关详细信息,建议您阅读Apple documentation about KVO。
答案 2 :(得分:1)
我建议使用dispatch_async
命令。
基本上,您将为代码添加一定级别的多线程功能。我可能错了,但我会重写你的代码:
- (void)viewDidLoad {
[super viewDidLoad];
self.sensorGyro = [MSBSensorUV createObjectGyro];
if(self.sensorGyro){
while (!self.sensorGyro.client.isDeviceConnected) {
NSLog(@"Awaiting connection");
}//Check that connection has been made
NSLog(@"connection UP");
dispatch_async(dispatch_get_main_queue(), ^{
[self.sensorGyro.client.sensorManager startGyroUpdatesToQueue:nil errorRef:nil withHandler:^(MSBSensorGyroData *GyroData, NSError *error) {
NSLog(@"Starting updates");
self.currentGyroLabel.text = [NSString stringWithFormat:@"%lu", GyroData.GyroIndexLevel];
});
}];