我理解错误是什么,但在这种情况下不知道是什么导致它。一般情况下,这种情况只会出现1%(可能更少),但我发现了一种极端的方式来引起这种情况,我将在下面描述。首先,我使用了我在Ray Wenderlich网站上发现的应用内购买流程。下面是这里的具体关注点:
. h:
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
@interface IAPHelper : NSObject
- (void)requestProductsWithCompletionHandler:RequestProductsCompletionHandler)completionHandler;
@end
m
@implementation IAPHelper
{
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(@"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(@"Found product: %@ %@ %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts); // here is where bad access occurs
_completionHandler = nil;
}
同样,99%以上的情况下,这很好地工作了。鉴于不良访问在日常使用中很少发生,并且很难诊断。然而,我发现了一种极端的方法来引起这个问题。设置是"Tab 1"是一个表视图控制器,"Tab 2"是一个表视图控制器,使用上面的代码。如果我在两个选项卡之间快速切换,通常会导致问题在几秒到20-30秒之间的任何地方出现。在这种情况下并不是每次都发生,但绝大多数情况都发生了。如上所示,当Parent为Null时,下一行会出现错误访问。
_completionHandler(是的,skProducts);
解决这个问题的方法如下:
if (_completionHandler)
{
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
虽然修复确实工作,并解决了问题,我仍然困扰为什么会发生这种情况。你知道这是什么原因吗?
更新:向所有人道歉,因为我忘记在上面粘贴以下内容。
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
在存储完成块时,需要像对待任何其他对象一样对待它。因此,如果您将块存储为变量,然后在与分配它的地方不同的范围内使用它,则需要通过复制或保留它来增加引用计数。简单的解决方案是创建一个strong
属性来存储您的块。
取决于不可见的代码位,您的完成处理程序块可能没有被正确分配。如果您打算在创建块的作用域之外使用它,则需要复制它。
在你的接口中,声明你的完成处理程序的存储属性为"copy"。
@property (nonatomic, readwrite, copy) void (^completionHandler)(BOOL, NSArray *);
如果你想控制这个局部变量,你可以在你的实现中手动合成这个属性:
@synthesize completionHandler = _completionHandler;