EXC_BAD_ACCESS用于在块内创建的对象

时间:2016-02-03 07:37:09

标签: objective-c cocoa-touch objective-c-blocks grand-central-dispatch

我总是对块和GCD感到紧张,因为我的头脑告诉我它看起来很复杂!

我在一个街区遇到了崩溃,理想情况下我看起来很好:

#pragma mark -
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
    __weak VTVehicleServiceNetworkManager *weakSelf = self;
    TaskBlock fetchOrdersListTaskBlock = ^()
    {
        __block __strong HLOrdersDataProvider *ordersDataProvider = nil;
        NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:[^{
            ordersDataProvider = [[HLOrdersDataProvider alloc] init];
            [ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
                                                   completionBlock:completionBlock
                                                        errorBlock:errorBlock];
        } copy]];
        [weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
    };

    [self fetchDataWithTaskBlock:[fetchOrdersListTaskBlock copy]
                      errorBlock:^(NSError *error) {
                          errorBlock(error);
                      }];
}

我能够追踪僵尸物体,但我无法弄清楚为什么这个物体变成了僵尸。以下是个人资料的快照:

enter image description here

我已经浏览了以下指南(12),看看我是否能找出我做错了什么,但我不知道发生了什么问题。< / p>

我所做错的任何帮助和参考文本都会有所帮助。

修改: 我已经尝试了@Jerimy的建议,事实上我之前发布的代码与所需的完全相同:在块操作本身内声明并初始化ordersDataProvider。但是由于它在同一时刻崩溃,我试图在块外面声明它,看它是否解决了崩溃问题。

以下是我测试的新代码:

#pragma mark -
-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
    __weak VTVehicleServiceNetworkManager *weakSelf = self;
    completionBlock = [completionBlock copy];
    errorBlock = [errorBlock copy];
    TaskBlock fetchOrdersListTaskBlock = ^()
    {
        NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:^{
            HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
            [ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
                                                   completionBlock:completionBlock
                                                        errorBlock:errorBlock];
        }];
        [weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
    };

    [self fetchDataWithTaskBlock:[fetchOrdersListTaskBlock copy]
                      errorBlock:^(NSError *error) {
                          errorBlock(error);
                      }];
}

来自个人资料的崩溃:

enter image description here

堆栈跟踪也没有太多,SDMHTTPRequest是一个库,我非常确定那里没有错,HLOrdersDataProvider是我能够追溯的僵尸对象在仪器应用程序:

enter image description here

编辑2 添加HLOrdersDataProvider的界面和实现以获取更多详细信息:

@interface HLOrdersDataProvider : HLDataProvider

-(void)performFetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock;

@end

@implementation HLOrdersDataProvider

-(void)performFetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
    // Using SDMConnectivity
    NSString *queryString = [infoDict valueForKey:@"$filter"];
    NSString *appendStringForEndpoint = [kRowsetsKeyword stringByAppendingFormat:@"?%@", queryString];
    [self fetchDataFromServerWithEndPointAppendString:appendStringForEndpoint
                                      completionBlock:completionBlock
                                           errorBlock:errorBlock];
}

#pragma mark - Service Agent related
-(NSString*)collectionName
{
    return [NSString stringWithString:kRowsetsKeyword];
}

-(void)requestFinished:(SDMHttpRequest *)request
{
    NSError *error = nil;
    // Let's parse the response and send the results back to the caller
    NSString *collectionName = [self collectionName];
    NSData *responseData = [request responseData];
    NSArray *entitiesArray = [self parseODataEntriesWithData:responseData
                                          withCollectionName:collectionName
                                                       error:&error];
    if (error)
        [self triggerFailureBlockWithArgument:error];
    else
        [self triggerCompletionBlockWithArgument:entitiesArray];
}

@end

此外,HLOrdersDataProvider继承自HLDataProvider,因此以下是此类的接口和实现:

#import <Foundation/Foundation.h>
//#import "SDMHttpRequestDelegate.h"
#import "SDMRequestBuilder.h"
#import "SDMHttpRequest.h"
#import "SDMParser.h"
#import "HLConstant.h"
#import "HLConnectionData.h"

@interface HLDataProvider : NSObject <SDMHttpRequestDelegate>

@property (copy, atomic) CompletionBlock completionBlock;
@property (copy , atomic) ErrorBlock errorBlock;
@property (copy, atomic) CompletionBlockWithDataFetchStatus completionBlockWithDataFetchStatus;
+ (id)sharedInstance;
- (NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName;
- (NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName error:(NSError**)outError;
- (NSMutableArray*)parseJSONEntriesWithData:(NSData*)data;

-(NSArray*)fetchEntriesFromDatabaseWithEntityName:(NSString*)entityName relationshipObjects:(NSMutableArray*)relationships;

-(void)updateDatabaseWithEntries:(NSMutableArray*)scanEntries;
-(void)updateDatabaseWithJSONEntries:(NSMutableArray*)scanEntries;

-(id)parsedOdataResultFromEntries:(NSMutableArray*)entries;

-(void)fetchDataFromServerWithEndPointAppendString:(NSString*)appendStr completionBlock:(CompletionBlock)inCompletionBlock errorBlock:(ErrorBlock)inErrorBlock;

-(NSString*)collectionName;
-(void)triggerCompletionBlockWithArgument:(id)inArg;
-(void)triggerFailureBlockWithArgument:(NSError*)inArg;
@end


@implementation HLDataProvider
+ (id)sharedInstance
{
    //Subclassess will override this method
    return nil;
}
-(NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName
{
    return [self parseODataEntriesWithData:data
                        withCollectionName:collectionName
                                     error:NULL];
}

-(NSMutableArray*)parseODataEntriesWithData:(NSData*)data withCollectionName:(NSString*)collectionName error:(NSError**)outError
{
    NSMutableArray *entriesArray = nil;

    @try {
        entriesArray = sdmParseODataEntriesXML(data,
                                               [[[HLConnectionData metaDataDocument] getCollectionByName:collectionName] getEntitySchema],
                                               [HLConnectionData serviceDocument]);
    }
    @catch (NSException *exception) {
        NSLog(@"Got exception: %@", exception);
        if (outError)
        {
            *outError = [NSError errorWithDomain:@"Vehicle Service"
                                            code:-1001
                                        userInfo:[NSDictionary dictionaryWithObject:exception  forKey:NSLocalizedDescriptionKey]];
        }
    }
    @finally {

    }
    return entriesArray;
}

- (NSMutableArray*)parseJSONEntriesWithData:(NSData*)data
{
    NSError *error = nil;
    id object = [NSJSONSerialization
                 JSONObjectWithData:data
                 options:0
                 error:&error];

    NSMutableArray *resultArray = nil;

    if(error) { /* JSON was malformed, act appropriately here */ }
    if([object isKindOfClass:[NSDictionary class]])
    {
        resultArray = [NSMutableArray arrayWithObject:object];
    }
    else if ([object isKindOfClass:[NSArray class]])
    {
        resultArray = [NSMutableArray arrayWithArray:object];
    }

    return resultArray;
}


#pragma mark -
#pragma mark - Data Fetch - Server - SDMConnectivity
-(void)fetchDataFromServerWithEndPointAppendString:(NSString*)appendStr completionBlock:(CompletionBlock)inCompletionBlock errorBlock:(ErrorBlock)inErrorBlock;
{
    self.errorBlock = inErrorBlock;
    self.completionBlock = inCompletionBlock;

    id<SDMRequesting> request = nil;

    //NSString *clientStr = @"&sap-client=320&sap-language=EN";

    NSString *urlStr =[NSString stringWithFormat:@"%@/%@",[HLConnectionData applicationEndPoint], appendStr];
    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [SDMRequestBuilder setRequestType:HTTPRequestType];
    request=[SDMRequestBuilder requestWithURL:[NSURL URLWithString:urlStr]];
    [request setUsername:kUserName];
    /*Set Password in SDMRequesting object*/
    [request setPassword:kPassword];
    [request setRequestMethod:@"GET"];
    [request setTimeOutSeconds:kTimeoutInterval];

    /*set the Delegate. This class must adhere to SDMHttpRequestDelegate to get the callback*/
    [request setDelegate:self];

    /*Call  startAsynchronous API to request object to retreive Data asynchrnously in the call backs  */
    [request startSynchronous];
}

-(void)updateDatabaseWithEntries:(NSMutableArray*)scanEntries
{
    //Subclasses will override this
}

-(void)updateDatabaseWithJSONEntries:(NSMutableArray*)scanEntries
{
    //Subclasses will override this
}


-(id)parsedOdataResultFromEntries:(NSMutableArray*)entries
{
    //Subclasses will override this
    return nil;
}

-(void)deleteExistingEntriesFromCoredata
{
    //Subclasses will override this
}

-(NSArray*)fetchEntriesFromDatabaseWithEntityName:(NSString*)entityName relationshipObjects:(NSMutableArray*)array
{
    //Subclasses will overide this method
    return nil;
}

#pragma mark - SDMHTTPRequestDelegate methods
- (void)requestStarted:(SDMHttpRequest*) request
{

}
- (void)requestFinished:(SDMHttpRequest*) request
{
    // For service doc and metadata we instantiate HLDataProvider, so we send this raw SDMHTTPRequest object as-is. For other service agents like HLOrdersDataProvider we send the parsed information, check the subclass' implementation of -requestFinished: method
    [self triggerCompletionBlockWithArgument:request];
}
-(void)triggerCompletionBlockWithArgument:(id)inArg
{
    self.completionBlock(inArg);
}
- (void)requestFailed:(SDMHttpRequest*) request
{
    [self triggerFailureBlockWithArgument:request.error];
}
-(void)triggerFailureBlockWithArgument:(NSError*)inArg
{
    self.errorBlock(inArg);
}
- (void)requestRedirected:(SDMHttpRequest*) request
{

}

#pragma mark - Service Agent related
-(NSString*)collectionName
{
    // Should be overridden by the subclasses
    return nil;
}

1 个答案:

答案 0 :(得分:1)

引用的代码非常复杂(你正在以非常复杂的方式做事)。

首先,您不应该在调用者中创建块的副本。必要时在被调用者中创建副本(即:如果要在弹出堆栈后调用它,则将其复制到堆而不是使用堆栈分配的块)。在几乎所有使用块的API中,确保块都在堆上并不是调用者的责任。

您的ordersDataProvider变量没有理由在fetchOrdersListTaskBlock的范围内声明,因为它只在fetchOrdersOperation的块中使用。

我没有立即看到崩溃的原因,但我怀疑简化代码有助于揭示问题。也许问题出在HLOrdersDataProvider的初始化程序中。

尝试类似:

-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
    completionBlock = [completionBlock copy];
    errorBlock = [errorBlock copy];

    __weak VTVehicleServiceNetworkManager *weakSelf = self;
    TaskBlock fetchOrdersListTaskBlock = ^{
        NSBlockOperation *fetchOrdersOperation = [NSBlockOperation blockOperationWithBlock:^{
            HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
            [ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
                                                   completionBlock:completionBlock
                                                        errorBlock:errorBlock];
        }];
        [weakSelf.dataOperationQueue addOperation:fetchOrdersOperation];
    };

    [self fetchDataWithTaskBlock:fetchOrdersListTaskBlock
                      errorBlock:errorBlock];
}

或者更好的是,重新设计你的课程就像这样工作(我不认为你的例子中需要NSBlockOperation):

-(void)fetchOrdersListWithInfoDict:(NSDictionary*)infoDict completionBlock:(CompletionBlock)completionBlock errorBlock:(ErrorBlock)errorBlock
{
    completionBlock = [completionBlock copy];
    errorBlock = [errorBlock copy];

    TaskBlock fetchOrdersListTaskBlock = ^{
        HLOrdersDataProvider *ordersDataProvider = [[HLOrdersDataProvider alloc] init];
        [ordersDataProvider performFetchOrdersListWithInfoDict:infoDict
                                               completionBlock:completionBlock
                                                    errorBlock:errorBlock];
    };

    [self fetchDataWithTaskBlock:fetchOrdersListTaskBlock
                      errorBlock:errorBlock];
}