【发布时间】:2014-09-02 10:11:08
【问题描述】:
遇到了一个很难解决的崩溃问题。我的 iOS 应用程序(iOS 版本 6+,Xcode 5.1.1)在用户从他的帐户注销时崩溃,但只有在它之前被前台和后台关闭时才会崩溃。
这是来自 Testflight 的崩溃日志:
SIGSEGV
APP_NAME copy__destroy_helper_block_
in CAPServiceManager.m on Line 0
# Binary Image Name Address Symbol
0 APP_NAME copy 0x0010b61c testflight_backtrace
1 APP_NAME copy 0x0010ae5c TFSignalHandler
2 libsystem_platform.dylib 0x33d0087a _sigtramp
3 APP_NAME copy 0x000f180c __destroy_helper_block_ in CAPServiceManager.m on Line 0
4 libsystem_blocks.dylib 0x33bdbae0 _Block_release
5 Foundation 0x27268eb8
6 libobjc.A.dylib 0x33650d5e
7 Foundation 0x272ff372
8 libdispatch.dylib 0x33ba295e
9 libdispatch.dylib 0x33ba5ba6 _dispatch_main_queue_callback_4CF
10 CoreFoundation 0x2655fbd8
11 CoreFoundation 0x2655e2d8
12 CoreFoundation 0x264ac610 CFRunLoopRunSpecific
13 CoreFoundation 0x264ac422 CFRunLoopRunInMode
14 GraphicsServices 0x2da060a8 GSEventRunModal
15 UIKit 0x29bf6484 UIApplicationMain
16 APP_NAME copy 0x0009587a main in main.m on Line 16
17 libdyld.dylib 0x33bc0aae
但在 Xcode 中,它作为 AppDelegate 文件中的 EXC_BAD_ACCESS 崩溃。 (没有提供更多细节)。启用 NSZombie 也不起作用,因为它可以防止应用在启用时崩溃。
CAPServiceManager中与block相关的代码如下:
- (void)executeService:(WCServiceType)service
pin:(NSString *)pin
payLoad:(id)payload
usingBlock:(void (^) (NSError *error))block
{
[self addStartingServiceStatusForService:service];
[[WCWebService sharedWebService]
postAuthTokenForService:service
pin:pin
vehicle:_vehicle
target:self
usingBlock:^(NSError *error, id response) {
if (!error) {
[self executeService:service token:response payLoad:payload usingBlock:block];
}
else {
if (error.code == kWCHTTPStatusCodeUnauthorized) {
_wrongPinCounter++;
}
[self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:NO];
block(error);
}
}];
}
- (void)executeService:(WCServiceType)service
token:(NSString *)token
payLoad:(id)payload
usingBlock:(void (^) (NSError *error))block
{
[self addStartingServiceStatusForService:service];
[[WCWebService sharedWebService]
postStartServiceWithTarget:self
service:service
payLoad:payload
token:token
vehicle:_vehicle
usingBlock:^(id target, NSError *error, WCServiceStatus *serviceStatus) {
if (!error) {
WCServiceStatus *startingServiceStatus = [self serviceStatusForService:service];
NSManagedObjectContext *moc = _vehicle.managedObjectContext;
[moc performBlockAndWait:^{
startingServiceStatus.sentPayload = payload;
[startingServiceStatus updateWithServiceStatus:serviceStatus];
}];
block(nil);
}
else {
if (error.localizedDescription && [error.localizedDescription isEqualToString:@"Service is already started"]) {
[self serviceIsAlreadyStartedForServiceType:service block:^(NSError *error) {
if (error) {
[self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:YES];
}
block(error);
}];
}
else {
[self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:NO];
block(error);
}
}
}];
}
我首先认为错误与自我有关。但是测试存储 self 也不起作用:
__weak CAPServiceManager *weakSelf = self;
也没有用。我还尝试将__block 作为修饰符。
如您所见,这些块被传递。然后将它们存储在 WCWebServiceRequest.m 中的实例变量中,如下所示:
_block = [block copy];
..._block 定义为
@interface WCWebServiceRequest : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
@protected
id _block;
//...
...后来用这个代码调用:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSOperationQueue *queue = [NSOperationQueue new];
queue.name = [NSString stringWithFormat:@"%s Queue", __PRETTY_FUNCTION__];
[queue addOperationWithBlock:^{
NSError *error = nil;
NSDictionary *attributes;
__block WCServiceStatus *serviceStatus;
if (_data) {
attributes = [NSJSONSerialization JSONObjectWithData:_data options:0 error:&error];
}
if (self.response.statusCode == kWCHTTPStatusCodeAccepted || self.response.statusCode == kWCHTTPStatusCodeOK) {
if ([attributes isKindOfClass:[NSDictionary class]]) {
NSManagedObjectContext *moc = [WCStorage sharedStorage].moc;
[moc performBlockAndWait:^{
serviceStatus = [WCServiceStatus makeServiceStatusWithAttributes:attributes moc:moc];
}];
}
else {
error = [NSError errorWithWebServiceErrorCode:kWCHTTPStatusCodeInvalidData];
}
}
else {
error = [NSError errorWithWebServiceErrorCode:self.response.statusCode errorInfo:[NSError errorInfoFromData:_data]];
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (!_cancelled) {
WCWebServiceServiceStatusBlock_Invoke(_block, _target, error, serviceStatus);
}
[super connectionDidFinishLoading:connection];
}];
}];
}
... WCWebServiceServiceStatusBlock_Invoke 定义为
#define WCWebServiceServiceStatusBlock_Invoke(block, target, error, serviceStatus) \
{ \
WCWebServiceServiceStatusBlock block_ = (WCWebServiceServiceStatusBlock) block; \
block_(target, error, serviceStatus); \
}
typedef void (^WCWebServiceServiceStatusBlock) (id target, NSError *error, WCServiceStatus *serviceStatus);
...和 _block 像这样被释放:
- (void)dealloc
{
if (_block) {
_block = nil;
}
}
知道可能出了什么问题或如何进一步调试吗?
编辑:我正在使用 ARC,我无法回答为什么要这样实现,我接手了一个现有项目。
【问题讨论】:
-
1.来自
(void (^) (NSError *error))block的块是否在WCWebServiceServiceStatusBlock_Invoke中被调用? -
2.为什么不将ivar _block定义为
WCWebServiceServiceStatusBlock? -
3.如果你使用ARC,那么你不需要
copy,也不需要dealloc。如果不是,那么 dealloc 将是:- (void) dealloc { [_block release]; [super dealloc];} -
感谢您的回复。 1.是的,它被称为,请参阅我问题中的倒数第二个代码部分。 2. 我会试试的。 3.将尝试删除dealloc并复制。然而,类似的问题似乎与 not 复制您的块有关。 IE。 stackoverflow.com/questions/23573114/…
-
删除 [block copy] 或删除 dealloc 对我的问题没有任何影响。我无法更改 _block 的类型,因为它并不总是存储 WCWebServiceServiceStatusBlock(其他类继承该类)。
标签: ios objective-c xcode crash objective-c-blocks