从 2.1 更新到 2.2 后 WCSession sendmessage 慢了一倍
WCSession sendmessage is twice as slow after updating from 2.1 to 2.2
我开发的 iOS 应用程序有一个与之配套的 Apple Watch 应用程序。我们最近开始收到关于 GPS 距离更新速度变慢并且手表比 phone 晚几秒的投诉。我一直在研究这个并写了一些测试代码,来自 [[WCSession defaultSession] sendMessage:message
replyHandler:replyHandler
errorHandler:errorHandler
的回复块
在 watchOS 2.2 中绝对比 2.1 慢两倍。我附上了下面的测试代码。
#pragma mark - Location Update.
/**
* @description Provides an NSBlockOperation to be executed in an operation queue. This is an attempt to force serial
* processing
*/
- (NSBlockOperation*)distanceUpdateBlock {
NSBlockOperation *blockOp = [[NSBlockOperation alloc] init];
__weak NSBlockOperation * weakOp = blockOp;
__weak typeof(self) weakSelf = self;
[blockOp addExecutionBlock:^{
typeof(weakSelf) blockSafeSelf = weakSelf;
typeof(weakOp) blockSafeOp = weakOp;
if (!blockSafeOp.isCancelled) { // Make sure we haven't already been cancelled.
__block NSDate *startDate = [NSDate date];
__block BOOL completed = NO;
void (^replyBlock)(NSDictionary*) = ^(NSDictionary *message){
if (!blockSafeOp.isCancelled) {
[blockSafeSelf processUserLocationOnWatch:message];
double replyTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Reply Time: %.03f", replyTime);
completed = YES;
}
};
void (^failBlock)(NSError*) = ^(NSError *error) {
if (!blockSafeOp.isCancelled) {
double replyTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Reply Time Fail: %.03f", replyTime);
completed = YES;
}
};
[self fetchUserLocationFromIphoneWithReplyHandler:replyBlock errorHandler:failBlock];
do {
usleep(10000); // 1/100th second wait to throttle evaluations (Don't worry - in final code I will subclass NSOperation and control when it actually finishes - this is for easy testing.)
} while (!completed && !blockSafeOp.isCancelled && [blockSafeSelf isWatchReachable]); //(isWatchReachable just makes sure we have a session with the phone and it is reachable).
}
}];
blockOp.completionBlock = ^{
typeof(weakSelf) blockSafeSelf = weakSelf;
typeof(weakOp) blockSafeOp = weakOp;
if (!blockSafeOp.isCancelled) {
[blockSafeSelf addOperationForLocationUpdate]; // since we are finished - add another operation.
}
};
return blockOp;
}
- (void)addOperationForLocationUpdate {
[distanceUpdateOperationQueue addOperation:[self distanceUpdateBlock]];
}
- (void)startUpdatingLocation {
[self addOperationForLocationUpdate];
}
- (void)stopUpdatingLocation {
[distanceUpdateOperationQueue cancelAllOperations];
}
- (void)fetchUserLocationFromIphoneWithReplyHandler:(nullable void (^)(NSDictionary<NSString *, id> *replyMessage))replyHandler errorHandler:(nullable void (^)(NSError *error))errorHandler {
if (self.isSessionActive) {
NSDictionary *message = @{kWatchSessionMessageTag:kWatchSessionMessageUserLocation};
if (self.isWatchReachable) {
[[WCSession defaultSession] sendMessage:message
replyHandler:replyHandler
errorHandler:errorHandler
];
} else {
errorHandler(nil);
}
} else {
[self activateSession];
errorHandler(nil);
}
}
iPhone 端的处理程序简单地获取用户位置并使用编码信息调用 replyHandler。
2.2 上的时间日志看起来像(始终大约一秒)
回复时间:0.919
回复时间:0.952
回复时间:0.991
回复时间:0.981
回复时间:0.963
2.1 上的相同代码看起来像
回复时间:0.424
回复时间:0.421
回复时间:0.433
回复时间:0.419
此外,我注意到在 5 分钟(300 秒)后,错误处理程序开始在已经调用回复处理程序的消息上被调用。我看到另一个帖子有人也提到了这个,有没有其他人发生过这种情况并且知道为什么?
那么,Q1 - 有没有人 运行 遇到这种性能下降并想出如何使 replyHandler 运行 更快,或者找到更快的方法来获取更新?
Q2 - 5 分钟后调用 errorHandler 的解决方案。
只需消除几件事 - 我已尽职调查在接收消息和调用 replyHandler 之间测试 iOS 代码。 iOS 9.2/9.3 之间的处理时间没有变化。我已将范围缩小到这个电话。事实上,我们在以前的版本中这样做的方式现在是备份sendMessage 的operationQueue。所以现在我强制使用此测试代码一次调用一个。我们不再备份了,但是个人调用很慢。
所以,我今天 运行 测试了同一段代码,使用相同的设备,虽然代码没有改变,但现在 运行 是最初速度的两倍做了(在 2.1 中)。日志在 0.12 - 0.2 秒的范围内到达。从那时起唯一发生的事情就是软件更新。此外,5 分钟后不再调用失败块。所以这个问题的两个部分都神奇地起作用了。我目前使用的是 iOS 9.3.4(13G35),手表是 2.2.2。似乎是处理手表和 phone 之间的队列链中某个地方的 OS 问题。现在一切都很好。
我开发的 iOS 应用程序有一个与之配套的 Apple Watch 应用程序。我们最近开始收到关于 GPS 距离更新速度变慢并且手表比 phone 晚几秒的投诉。我一直在研究这个并写了一些测试代码,来自 [[WCSession defaultSession] sendMessage:message
replyHandler:replyHandler
errorHandler:errorHandler
在 watchOS 2.2 中绝对比 2.1 慢两倍。我附上了下面的测试代码。
#pragma mark - Location Update.
/**
* @description Provides an NSBlockOperation to be executed in an operation queue. This is an attempt to force serial
* processing
*/
- (NSBlockOperation*)distanceUpdateBlock {
NSBlockOperation *blockOp = [[NSBlockOperation alloc] init];
__weak NSBlockOperation * weakOp = blockOp;
__weak typeof(self) weakSelf = self;
[blockOp addExecutionBlock:^{
typeof(weakSelf) blockSafeSelf = weakSelf;
typeof(weakOp) blockSafeOp = weakOp;
if (!blockSafeOp.isCancelled) { // Make sure we haven't already been cancelled.
__block NSDate *startDate = [NSDate date];
__block BOOL completed = NO;
void (^replyBlock)(NSDictionary*) = ^(NSDictionary *message){
if (!blockSafeOp.isCancelled) {
[blockSafeSelf processUserLocationOnWatch:message];
double replyTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Reply Time: %.03f", replyTime);
completed = YES;
}
};
void (^failBlock)(NSError*) = ^(NSError *error) {
if (!blockSafeOp.isCancelled) {
double replyTime = [[NSDate date] timeIntervalSinceDate:startDate];
NSLog(@"Reply Time Fail: %.03f", replyTime);
completed = YES;
}
};
[self fetchUserLocationFromIphoneWithReplyHandler:replyBlock errorHandler:failBlock];
do {
usleep(10000); // 1/100th second wait to throttle evaluations (Don't worry - in final code I will subclass NSOperation and control when it actually finishes - this is for easy testing.)
} while (!completed && !blockSafeOp.isCancelled && [blockSafeSelf isWatchReachable]); //(isWatchReachable just makes sure we have a session with the phone and it is reachable).
}
}];
blockOp.completionBlock = ^{
typeof(weakSelf) blockSafeSelf = weakSelf;
typeof(weakOp) blockSafeOp = weakOp;
if (!blockSafeOp.isCancelled) {
[blockSafeSelf addOperationForLocationUpdate]; // since we are finished - add another operation.
}
};
return blockOp;
}
- (void)addOperationForLocationUpdate {
[distanceUpdateOperationQueue addOperation:[self distanceUpdateBlock]];
}
- (void)startUpdatingLocation {
[self addOperationForLocationUpdate];
}
- (void)stopUpdatingLocation {
[distanceUpdateOperationQueue cancelAllOperations];
}
- (void)fetchUserLocationFromIphoneWithReplyHandler:(nullable void (^)(NSDictionary<NSString *, id> *replyMessage))replyHandler errorHandler:(nullable void (^)(NSError *error))errorHandler {
if (self.isSessionActive) {
NSDictionary *message = @{kWatchSessionMessageTag:kWatchSessionMessageUserLocation};
if (self.isWatchReachable) {
[[WCSession defaultSession] sendMessage:message
replyHandler:replyHandler
errorHandler:errorHandler
];
} else {
errorHandler(nil);
}
} else {
[self activateSession];
errorHandler(nil);
}
}
iPhone 端的处理程序简单地获取用户位置并使用编码信息调用 replyHandler。
2.2 上的时间日志看起来像(始终大约一秒)
回复时间:0.919
回复时间:0.952
回复时间:0.991
回复时间:0.981
回复时间:0.963
2.1 上的相同代码看起来像
回复时间:0.424
回复时间:0.421
回复时间:0.433
回复时间:0.419
此外,我注意到在 5 分钟(300 秒)后,错误处理程序开始在已经调用回复处理程序的消息上被调用。我看到另一个帖子有人也提到了这个,有没有其他人发生过这种情况并且知道为什么?
那么,Q1 - 有没有人 运行 遇到这种性能下降并想出如何使 replyHandler 运行 更快,或者找到更快的方法来获取更新?
Q2 - 5 分钟后调用 errorHandler 的解决方案。
只需消除几件事 - 我已尽职调查在接收消息和调用 replyHandler 之间测试 iOS 代码。 iOS 9.2/9.3 之间的处理时间没有变化。我已将范围缩小到这个电话。事实上,我们在以前的版本中这样做的方式现在是备份sendMessage 的operationQueue。所以现在我强制使用此测试代码一次调用一个。我们不再备份了,但是个人调用很慢。
所以,我今天 运行 测试了同一段代码,使用相同的设备,虽然代码没有改变,但现在 运行 是最初速度的两倍做了(在 2.1 中)。日志在 0.12 - 0.2 秒的范围内到达。从那时起唯一发生的事情就是软件更新。此外,5 分钟后不再调用失败块。所以这个问题的两个部分都神奇地起作用了。我目前使用的是 iOS 9.3.4(13G35),手表是 2.2.2。似乎是处理手表和 phone 之间的队列链中某个地方的 OS 问题。现在一切都很好。