0

對於塊和GCD,我一直很緊張,因爲我的大腦告訴我它看起來非常複雜!EXC_BAD_ACCESS在塊內創建的對象

我得到一個塊,最好看起來好了給我的內心崩潰:

#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)去看看,如果我能找出我做錯了,但我沒有在附近發現什麼出錯了。

任何幫助和參考文字,我什麼做錯了將有所幫助。

編輯: 我已經試過什麼@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是我能夠在Instruments應用程序中找到的殭屍對象:

enter image description here

EDIT 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 

此外,HLOrdersDataProviderHLDataProvider所以下面繼承是這個類的接口和實現過:

#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; 
} 
+0

帖子堆棧跟蹤。 – trojanfoe

+0

@Raj在塊外的ordersDataProvider的任何特殊原因?爲什麼不只是把HLOrdersDataProvider * ordersDataProvider = [[HLOrdersDataProvider alloc] init];直接在塊內。 – Allen

+0

對不起,延遲迴復,我有orderDataDataProvider塊以外因爲它崩潰時,我有它。一旦我重新開始工作,我會發布堆棧跟蹤。 –

回答

1

被引用的代碼非常複雜(你正在做的事情非常c複製的方式)。

首先,你不應該在調用者中創建塊的副本。如有必要,在被調用者中複製副本(即:如果要在彈出堆棧後調用它,則將其複製到堆中,而不是使用堆棧分配的塊)。在幾乎所有使用塊的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]; 
} 
+0

「,這是在fetchOrdersListTaskBlock被釋放後執行的,因此ordersDataProvider變量不再被分配。」這是不正確的。只要某個範圍有權訪問該變量,就會分配它。 – newacct

+0

啊對了。 ordersDataProvider是__block,所以當引用塊(fetchOrdersOperation的塊)被複制到堆時,它將被移動到堆中。閱讀時我錯過了。感謝您指出了這一點。我的建議和代碼評論仍然存在。簡化代碼可能有助於揭示問題的根源。我將在上面進行編輯。 –

+0

我已經在上面的問題中添加了更多細節,我將不得不堅持操作隊列,因爲這是網絡層的設計,我將不得不遵守它。我已經檢查過你的第二種方法,並且該應用程序不會與殭屍對象問題崩潰,所以肯定有一些與塊內的操作塊有關。我打破了我的頭! –