Weird NSZombie error in nsmutuablearray - objective-c

I had faced a weird error for a few days ago. Luckly, i had solved this error about in 4-5 day. However, i just wonder that the error why happen.
First of all, i will describe the situation with same sample of code.
#interface SampleViewController ()
#property (nonatomic, strong) NSMutableArray *selectedIssue;
#property (nonatomic, strong) NSOperationQueue *queueJSON;
#property (nonatomic, strong) NSOperationQueue *queueImage;
#end
#implementation SampleViewController
/** blah blah blah*/
- (void) fillIssueArrayMethodWithIssueId:(NSInteger) selectedId {
NSString *requestURL = [NSString stringWithFormat:#"%#Issues/Get/?id=%d", kAPIRootURL, selectedId];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestURL]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:kNetworkTimeOut];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id response) {
NSData *JSONData = [NSData dataWithContentsOfFile:filePath
options:NSDataReadingMappedIfSafe
error:nil];
NSMutableArray *responseObject = [NSJSONSerialization JSONObjectWithData:JSONData
options:NSJSONReadingMutableContainers
error:nil];
if(responseObject) {
if([responseObject isKindOfClass:[NSArray class]]) {
_selectedIssue = [NSMutableArray arrayWithArray:responseObject];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) { }];
[_queueJSON addOperation:op];
}
-(void)startDownloadImages {
NSArray *objPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *strPath = [objPaths lastObject];
if(!_queueImage)
_queueImage = [[NSOperationQueue alloc] init];
_queueImage.maxConcurrentOperationCount = 3;
NSBlockOperation *completionOperation = [NSBlockOperation new];
for (__block NSDictionary *object in _selectedIssue) {
// Photos
NSString *imgURL = object[#"ImageUrl"];
NSString *strImageFile = [NSString stringWithFormat:#"%#_%#", object[#"Id"], [imgURL lastPathComponent]];
__block NSString *strImagePath = [NSString stringWithFormat:#"%#/Images/Issues/%#", strPath, strImageFile];
NSURLRequest *request4Images = [NSURLRequest requestWithURL:[NSURL URLWithString:imgURL]];
AFHTTPRequestOperation *operation4Images = [[AFHTTPRequestOperation alloc] initWithRequest:request4Images];
[operation4Images setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id rawResult) {
BOOL isImageWrited = [rawResult writeToFile:strImagePath options:NSDataWritingAtomic error:nil];
if(isImageWrited) {
NSLog(#"Image Write Success : %#", operation.request.URL);
} else {
NSLog(#"Image Write Error: %#", imageWriteError.description);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image Write Failed : %# : %ld", operation.request.URL, (long)operation.response.statusCode);
}];
[completionOperation addDependency:operation4Images];
}
[_queueImage addOperations:completionOperation.dependencies waitUntilFinished:NO];
[_queueImage addOperation:completionOperation];
}
#end
Here is problem;
In application lifecycle, fillIssueArrayMethodWithIssueId method get JSON data to mutuablearray first. Then start to download images by getting image URL from the array.
Each second time, i want to access the _selectedIssue in fillIssueArrayMethodWithIssueId method, my app got crash (SIGSEGV)
In according to long investigation, _selectedUssue become instantly zombie object when StartDownloadImages method finished. However in this method i have never reallocated this array. I have just read values in this array. I am sure that the array become zombie object in this method so the issue fixed when removed "__block" in for loop.
So the question, __block type how could affect the _selectedIssue arrays object ?
I just want to know what i am missing...
By the way,i have been tried in StartDownloadImages method before the for loop, i had create temporary array and this temp array just initiated with _selectedIssue mutableCopy or just copy. However, i again faced with zombie problem.

Related

Loading Core Data From Large JSON Causing App To Crash

I'm attempting to populate CoreData from a JSON file that consists of 170,000 plus dictionaries. The parsing of the json goes quick but when I start trying to add to CoreData I'm blocking the main thread for a long time and then the app eventually crashes. It crashes when calling the method [UIDocument saveToUrl:forSaveOperation:completionHandler] Here is my code. If anyone has an idea of what's causing it to crash or a more efficient way to load CoreData that would be greatly appreciated.
#property (nonatomic, strong) UIManagedDocument *wordDatabase;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.wordDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Word Database"];
self.wordDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
}
- (void)setWordDatabase:(UIManagedDocument *)wordDatabase
{
if (_wordDatabase != wordDatabase) {
_wordDatabase = wordDatabase;
[self useDocument];
}
}
- (void)useDocument
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.wordDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.wordDatabase saveToURL:self.wordDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self prepopulateWordDatabaseWithDocument:self.wordDatabase];
}];
}
}
- (void)prepopulateWordDatabaseWithDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchQ = dispatch_queue_create("Word Fetcher", NULL);
dispatch_async(fetchQ, ^{
//Fetch the words from the json file
NSString *fileString = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"json"];
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:fileString encoding:NSUTF8StringEncoding error: NULL];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSArray *words = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
[document.managedObjectContext performBlock:^{
for (NSDictionary *dictionary in words)
{
[Word wordFromDictionary:dictionary inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
dispatch_release(fetchQ);
}
What I ended up doing that stopped my app from crashing was allocating a new NSManagedObjectContext and peformed all my loading in the background. After saving I called my NSFetchedResultsController and the table repopulated.
- (void)prepopulateWordDatabaseWithDocument:(UIManagedDocument *)document
{
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.undoManager = nil;
backgroundContext.persistentStoreCoordinator = document.managedObjectContext.persistentStoreCoordinator;
[backgroundContext performBlock:^{
NSString *fileString = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"json"];
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:fileString encoding:NSUTF8StringEncoding error: NULL];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *parseError;
NSArray *words = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&parseError];
for (NSDictionary *dictionary in words)
{
[Word wordFromDictionary:dictionary inManagedObjectContext:backgroundContext];
}
NSError *loadError;
if ([backgroundContext save:&loadError]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setupFetchedResultsController];
});
}
}];
}

Operations, methods and threads

I have some code that makes a http call to a json based webservice. That's working fine but I tried to move the code to it's own class and I have a slight hang up. When I call the method, the main thread just continues to the next command without wait for a response from my method.
Here's some code from the main part of the program
[newcall run];
NSLog(#"%#",[newcall status]);
NSArray *resultarray= [newcall returndata];
for (NSString *element in resultarray) {
NSLog(#"%#",element);
}
My Header
#import "AFHTTPClient.h"
#interface jsoncall : AFHTTPClient
{
NSString* Date;
NSString* apps;
NSString* data1;
NSURL* url;
NSString* Path;
NSArray* returndata;
NSString* status;
}
-(void) setApp: (NSString *)input;
-(void) setData: (NSString *)input;
-(void) setURL: (NSString *)input;
-(void) setPath: (NSString *)input;
-(int) run;
-(NSArray *) returndata;
-(NSString *) status;
#end
My run method
-(int) run
{
__block int success;
NSDictionary* jsonDictionary=[NSDictionary dictionaryWithObject: data1 forKey:#"data"];
NSString* jsonString = [jsonDictionary JSONRepresentation];
AFHTTPClient *httpClient=[[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *params =[NSDictionary dictionaryWithObjectsAndKeys:
apps,#"app",
jsonString,#"smpdata",nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:Path parameters:params];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSArray *dataarray=[JSON valueForKey:#"Data"];
status= [NSString stringWithFormat:#"%#",[JSON valueForKeyPath:#"Status"]];
NSLog(#"%#",status);
returndata= dataarray;
success=1;
NSLog(#"Success: Made it here");
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error,id JSON)
{
success=0;
NSLog(#"Error: Made it here");
}
];
[operation start];
[operation waitUntilFinished];
return success;
}
The method I used was Asychronous and as such I would have to have chained the next set of processing by calling it from the completion block. When the final app is done, we may do that but there's also this solution.
Waiting for completion block to complete in an AFNetworking request
By switching AFJSONRequestOperation to NSURLConnection, I am able to use Sychronous Mode which means the request has to complete before the thread moves on.
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:Path parameters:params];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error) {
NSLog(#"Error: %#",error);
}
else {
id JSON = AFJSONDecode(data, &error);
NSArray *dataarray=[JSON valueForKey:#"Data"];
status= [NSString stringWithFormat:#"%#",[JSON valueForKeyPath:#"Status"]];
NSLog(#"%#",status);
returndata= dataarray;
}
For our needs Synchronous mode should work fine for now.
However
You should not run a Synchronous web call on an UI thread because it will block and make your UI unresponsive. That means you need to put your call to this class in it's own thread with any other code that relies on it. I believe you want to check out use of NSOperation but I'm a Objective-c noob so I won't be adding an example here.

AFNetworking HTTPRequestOperation need to set array from completion block but this isn't working?

I'm using AFNetworking with AFHTTPRequestOperation to pull XML data from a webservice. This is working fine and im getting the data I need but I need to split this data into objects and initialize a NSMutableArray with this data. This is working in the completion block, but just before I return the array in my method the data is gone? How do I do this?
Here is some of my code:
NSMutableArray *result = [[NSMutableArray alloc] init];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString* response = [operation responseString];
NSData* xmlData = [response dataUsingEncoding:NSUTF8StringEncoding];
NSError *xmlError;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&xmlError];
NSArray *allElements = [doc.rootElement elementsForName:#"Misc"];
for (GDataXMLElement *current in allElements)
{
NSString *titel;
NSString *tekst;
NSArray *titels = [current elementsForName:#"Titel"];
if(titels.count > 0)
{
GDataXMLElement *firstTitel = (GDataXMLElement *) [titels objectAtIndex:0];
titel = firstTitel.stringValue;
} else continue;
NSArray *teksts = [current elementsForName:#"Tekst"];
if(teksts.count > 0)
{
GDataXMLElement *firstTekst = (GDataXMLElement *) [teksts objectAtIndex:0];
tekst = firstTekst.stringValue;
} else continue;
HVMGUniversalItem *item = [[HVMGUniversalItem alloc] initWithTitel:titel AndTekst:tekst];
[result addObject:item];
}
NSLog(#"%i", result.count);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [operation error]);
}];
[operation start];
NSLog(#"%i", result.count);
return result;
What am I doing wrong? Why isn't the data present in the array when returning?
Why isn't the data present in the array when returning?
Because AFNetworking use an async pattern. So the return code will be performed before the operation will be completed.
You need to use a different approach or follow Can AFNetworking return data synchronously (inside a block)?. The latter is discouraged.
A solution could be to:
-> Create a NSOperationQueue within your class that will include your operation. Create it as a property for your class like.
#property (nonatomic, strong, readonly) NSOperationQueue* downloadQueue;
- (NSOperationQueue*)downloadQueue
{
if(downloadQueue) return downloadQueue;
downloadQueue = // alloc init here
}
-> Create a property for your array (synthesize also it)
#property (nonatomic, strong) NSMutableArray* result;
-> Wrap your code within a specific method like doOperation.
self.result = [[NSMutableArray alloc] init];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
__weak YourClass* selfBlock = self;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString* response = [operation responseString];
NSData* xmlData = [response dataUsingEncoding:NSUTF8StringEncoding];
NSError *xmlError;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&xmlError];
NSArray *allElements = [doc.rootElement elementsForName:#"Misc"];
for (GDataXMLElement *current in allElements)
{
NSString *titel;
NSString *tekst;
NSArray *titels = [current elementsForName:#"Titel"];
if(titels.count > 0)
{
GDataXMLElement *firstTitel = (GDataXMLElement *) [titels objectAtIndex:0];
titel = firstTitel.stringValue;
} else continue;
NSArray *teksts = [current elementsForName:#"Tekst"];
if(teksts.count > 0)
{
GDataXMLElement *firstTekst = (GDataXMLElement *) [teksts objectAtIndex:0];
tekst = firstTekst.stringValue;
} else continue;
HVMGUniversalItem *item = [[HVMGUniversalItem alloc] initWithTitel:titel AndTekst:tekst];
[selfBlock.result addObject:item];
}
NSLog(#"%i", result.count);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [operation error]);
}];
[downloadQueue addOperation:operation];
-> if you need to notify that result has object send a notification, use the delegate pattern, etc...
Hope that helps.

return data from sendAsynchronousReques

Objective c: I have a serviceClass connecting to a db with NSURLConnection and sendAsynchronousRequest.
I want to be able to use this class method and return the db-data to any other classmethod requesting it.
But since the sendAsynchronousRequest is returning void - how can i do this?
I cant get my head around it at all atm. Blocks? But how...
Please help
atm I am creating a user object directly in this codechunk bellow:
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *urlResponse,
NSData *data,
NSError *error) {
if ([data length] > 0 && error == NULL){ // do success or error call with true or false
NSMutableData *incomingData;
if(!incomingData) {
incomingData = [[NSMutableData alloc]init];
}
[incomingData appendData:data];
NSString *string = [[NSString alloc]initWithData:incomingData
encoding:NSUTF8StringEncoding];
//incomingData = nil;
// create dictionary from json
NSData *jsondata = [NSData dataWithData:incomingData];
NSMutableDictionary *userDictionary = [NSJSONSerialization JSONObjectWithData:jsondata
options:NSJSONReadingMutableContainers
error:NULL];
// create user object
User *user = [User userFromDictionary:userDictionary];
NSLog(#"user back to object successfull!! %#", user);
}
}
You can use property for incomingData and then use that property to pass data to other methods/class.
#property (nonatomic, retain) NSMutableData * incomingData;
Now you can use self.incomingData to set value, and you need to return. You can use it in other methods, and when you need to pass this to other class, you can pass it.

reloadData Crashing iOS App

I'm doing a Twitter request for API data with:
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.json?q=a2zwedding&include_entities=true"] parameters:nil requestMethod:TWRequestMethodGET];
then I'm getting all processing the request with:
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
// NSString *output;
NSArray *results;
if ([urlResponse statusCode] == 200) {
NSError *jsonParsingError = nil;
NSDictionary *publicTimeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
results = [publicTimeline objectForKey:#"results"];
}
[self performSelectorOnMainThread:#selector(populateTable:) withObject:results waitUntilDone:YES];
}];
I'm then trying to display the "results" in a UITableView. My delegate and datasource are the same view controller that is processing the JSON data. My datasource method of:
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
is returning zero if I try to do the count of my array because my JSON parsing isn't finished when this gets called. If I "return 1" with this method, it properly displays one of the results from my Twitter request. However, if I use reloadData my app crashes. I cannot get it to delay the count. Any ideas?
in .h
//use this as your datasource
#property(nonatomic, strong)NSArray *myResultData;
.m
#synthesize myResultData;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
// NSString *output;
NSArray *results;
if ([urlResponse statusCode] == 200) {
NSError *jsonParsingError = nil;
NSDictionary *publicTimeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
results = [publicTimeline objectForKey:#"results"];
if(![results isKindOfClass:[NSNull class]])
{
myResultData = [NSArray alloc]initWithArray:results];
}
[self.tableView reloadData];
}
}];