I have a function where i use NSJSONSerialization. I want to do a good release / memory management because it can be 200+ objects.
for (NSDictionary *dict in visitsAndQuestionnaires) {
NSInputStream *is = [[NSInputStream alloc] initWithFileAtPath:filePath];
[is open];
if (is) {
JSON = [NSJSONSerialization JSONObjectWithStream:is options:0 error:nil];
if (![JSON respondsToSelector:#selector(objectForKey:)]) {
JSON = nil;
}
[is close];
}
[is release];
if (JSON) {
// HERE MY FUNCTION TO CREATE A LAYOUT BASED ON JSON
}
// WHEN FUNCTION WITH JSON IS DONE:
if (JSON) {
//JSON = NIL;
[(id)JSON release];
}
}
The function than does it function for 219 objects only on object 219 the application crashes with this console log:
-[CFDictionary release]: message sent to deallocated instance 0x26a13ca0
Even though im only releasing JSON when it still exists and this works (i can see it in profiler) only it looks like when it tries to release last object its already gone.
How can i fix this? So that this releas functions works?
The NSJSONSerialization class returns an autoreleased object. Just don't release it.
Creating an Autoreleasepool on the function loop was the solution. My thanks go out to you guys! Learned something today..
Related
Hi My problem is that i am getting a response from a web service and when i parse it and add to table and reload the table view it is not refreshing . Although if i call the [table reload] in keyboard is shown it gets updated there. Could someone tell me if im missing anything
This is what i am trying to do
- (void) longPoll {
//create an autorelease pool for the thread
#autoreleasepool {
NSLog(#"polling");
VSAppDelegate *var = (VSAppDelegate*)[[UIApplication sharedApplication] delegate];
//compose the request
NSError* error = nil;
NSHTTPURLResponse* response = nil;
//send the request (will block until a response comes back)
NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"polling response is %d",response.statusCode);
//pass the response on to the handler (can also check for errors here, if you want)
[self performSelectorOnMainThread:#selector(dataReceived:) withObject:responseData waitUntilDone:YES];
}
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) startPoll {
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) dataReceived: (NSData*) theData
{
//process the response here
NSError *error = nil;
NSLog(#"polling data is %#",[[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding]);
NSLog(#"polling data is %#",[[theData base64EncodedString]base64DecodedString]);
NSDictionary *notifDic= [NSJSONSerialization JSONObjectWithData:theData options:kNilOptions error:&error];
//VSViewControllerSplit *split = [[VSViewControllerSplit alloc]init];
[self RecieveFunction:notifDic];
}
try it
dispatch_async(dispatch_get_main_queue(), ^{
[tablrView reloaddata];
});
The dataReceived method doesn't appear to be calling reloadData. I'll assume that RecieveFunction method does, though, but you should confirm that. It's hard to say without seeing RecieveFunction.
The more fundamental issue would appear to be that dataReceived method is creating a new instance of VSViewControllerSplit, calling its RecieveFunction method, and then letting this new VSViewControllerSplit instance fall out of scope (and if using ARC, get deallocated unless you pushed to it, presented it, etc.). You presumably don't want to create a new VSViewControllerSplit every time longPoll calls dataReceived, but rather just reference the existing instance.
I'm not sure if i cause a leak here, is it ok to return allocated NSError back to
the calling method by perform selector?
Is it OK to create the NSMutableArray and store it in the same object i got for the callback? and later pass it to the delegate?
The code works fine, but because i'm new to arc i have the fear of doing something wrong.
(i'm using perform selector because my selector is dynamic. just for the example i wrote it statically).
AFHTTPRequestOperation *operation = [self.client HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//-----------------Callback--------------------
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
SEL callback = #selector(getOperationCallback:);
NSError *error = [self performSelector:callback withObject:operation];
//------------------Delegate Call---------------
if(operation.delegate)
[operation.delegate onFinish:operation.requestIdentifier error:error
data:operation.parsedObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//------------------Delegate Call---------------
if(operation.delegate)
[operation.delegate onFinish:operation.requestIdentifier error:error data:nil];
}];
- (NSError *)getOperationCallback:(AFHTTPRequestOperation *)operation{
NSArray *rawJson = (NSArray *)operation.jsonObject;
NSError *error;
NSMutableArray *array = [[NSMutableArray alloc] init];
for(id json in rawJson){
MyObject *object = [[MyObject alloc] initWithJson:json];
if(object){
[array addObject:object];
}else{
error = [NSError errorWithDomain:#"myErrors" code:1000 userInfo:nil];
break;
}
}
operation.parsedObject = array;
return error;
}
Generally, performSelector: in ARC could only cause a leak, if the selector you pass to it starts with alloc, new, retain, copy, or mutableCopy.
is it ok to return allocated NSError back to the calling method by perform selector?
Is it OK to create the NSMutableArray and store it in the same object
i got for the callback?
and later pass it to the delegate?
Answer to all questions are OK, nothing wrong with anything. All objects are created in autorelease way.
As long as the return value of the method your are invoking via performSelector:withObject: is an object, it's perfectly ok to do that.
It won't leak since ARC will take care of releasing array and error is autoreleased.
In general, steer clear of performSelector:. The reason being that ARC cannot help you. Even if you think your app works and you've tested that it does, it might break later down the line when you change something.
Sure, if the selector you're calling does not start with alloc, new, copy, mutableCopy, etc then it won't be a problem. But there's cases, such as using __attribute__((ns_returns_retained)) that make it non-obvious that a method might return something retained. In any case, having code that ARC cannot help you out with, is a bad thing.
There's always a way to make it such that you don't have to use performSelector:. Why not make use of a callback block for example?
I'm basically implementing a fancier NSURLConnection class that downloads data from a server parses it into a dictionary, and returns an NSDictionary of the data. I'm trying add a completion block option (in addition to a delegate option), but it crashes anytime I try to store that data in another class.
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
I can NSLog that data just fine, and basically do whatever I want with it, but as soon as I try to save it into another variable it crashes with a really obscure message.
EDIT: the crash message is EXC_BAD_ACCESS, but the stack trace is 0x00000000 error: address doesn't contain a section that points to a section in a object file.
I'm calling this function in the init method of a singleton. It DOES let me save the data if I set this in the completion block.
[SingletonClass sharedInstance].contentDictionary = data
But then the app gets stuck forever because sharedInstance hasn't returned yet, so the singleton object is still nil, so sharedInstance in the completion block calls init again, over and over.
EDIT 2: The singleton code looks like this:
+ (SingletonClass*)sharedInstance {
static SingletonClass *instance;
if (!instance) {
instance = [[SingletonClass alloc] init];
}
return instance;
}
- (id)init {
self = [super init];
if (self) {
dataFetcher_ = [[DataFetcher alloc] init];
NSString *testURL = #"..."
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
[SingletonClass sharedInstance].contentDictionary = data;
}];
}
return self;
}
Like I said, this works fine but repeats the initialize code over and over until the app crashes. This only happens the first time I run the app on a device, because I cache the data returned and it doesn't crash once I have the data cached. I would like to be able to just say self.contentDictionary = data, but that crashes.
Specify a variable to be used in the block with the __block directive outside of the block:
__block NSDictionary *contentDictionary_;
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
You're invoking recursion before ever setting the "instance". (which I now see you understand from OP).
In your block, you can use the ivar or an accessor instead of
[SingletonClass sharedInstance].contentDictionary
use:
_contentDictionary = [data copy]; or self.contentDictionary=data;
assuming that the ivar backing the contentDictionary property is _contentDictionary.
It sounds like you tried self.contentDictionary and it failed? I got it to work in a test, with ARC turned, so there may be something about your dataFetcher that is affecting this. In my test dataFetcher just returns a dictionary with a single element.
Turns out the issue was with a bunch of different parts. My URL was empty sometimes, and my data fetcher would just fail immediately and call the completion block. In my completion block I hadn't included any error handling, so if the singleton class hadn't initialized, it would repeat forever. With a real URL this doesn't happen.
I still would like to figure out why it crashes when I try to assign the data to an ivar, though.
I am using the class XMLReader to parse some XML from a URL. The XML is successfully parsed sometimes, and sometimes I get:
Error Domain=NSXMLParserErrorDomain Code=4 "The operation couldn’t be completed. (NSXMLParserErrorDomain error 4.)"
The parse is usually successful the first time I run it, after changing something, and it fails after that until I change something else. For example, in the code below, I tried commenting out the [parser release] line, and it parsed successfully. Then I ran it again and back to error code 4.
I log the same input data every time, success or fail.
Any ideas what is going wrong here? I can paste in more code if that would help, but I have isolated the error to be within the NSXMLParser parse method (called in the code below), because it always receives the same data.
Thanks!
edit: I know that error code 4 is an empty document error. But I know my NSData is not empty. So there is something else happening here
- (NSDictionary *)objectWithData:(NSData *)data
{
//data always makes it here, the same data gets logged regardless of parse success
//NSLog(#"%#",data);
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser setShouldResolveExternalEntities:NO];
BOOL success = [parser parse];
[parser release];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
The issue isn't whether data is correct at the beginning of your method objectWithData; it is whether data is correct through out the parse method computation. You should check that integrity of data after the parser completes.
In all likelihood, data is returned by the XMLReader but you are not retaining it properly. Occasionally the computationally intensive parser forces a garbage collection (or pool reclamation) and data gets corrupted.
Just do a [data retain] at the start of your objectwithData method; the problem will then disappear.
I get this warning in Xcode
warning: Attempting to create USE_BLOCK_IN_FRAME variable with block
that isn't in the frame.
Xcode redirect me to my NSStream
_naturStream = [[NSInputStream alloc] initWithData:natur];
It is random when it does this error, and my application crashes when it is triggered. Anyone tried similar problem ?
thanks
EDIT
in the appDelegate.h
#property (nonatomic, strong) NSInputStream *naturStream;
In the appDelegate.m:
NSData *natur = [NSData dataWithContentsOfURL:[NSURL URLWithString:_locString]];
_naturStream = [[NSInputStream alloc] initWithData:natur];
[_naturStream open];
if (_naturStream) {
NSError *parseError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithStream:_naturStream options:NSJSONReadingAllowFragments error:&parseError];
if ([jsonObject respondsToSelector:#selector(objectForKey:)]) {
for (NSDictionary *natur in [jsonObject objectForKey:#"results"]) {
_poi = [[POI alloc]init];
[_poi setTitle:[natur objectForKey:#"title"]];
[_poi setLat:[[natur objectForKey:#"lat"]floatValue]];
[_poi setLon:[[natur objectForKey:#"lng"]floatValue]];
[_poi setDistance:[natur objectForKey:#"distance"]];
[_poi setWebUrl:[natur objectForKey:#"webpage"]];
[_naturArray addObject:_poi];
}
}
}
else {
NSLog(#"Failed to open stream.");
}
[_naturStream close];
}
I realized that i forgot [_naturStream close] i don't know if it has solved the problem or not ?
EDIT
Another thing,.... I use a Thread for fetching the JSON data:
dispatch_queue_t jsonParsingQueue = dispatch_queue_create("jsonParsingQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(jsonParsingQueue, ^{
[self parseJSON];
dispatch_async(dispatch_get_main_queue(), ^{
[_kortvisning updateAnno];
[visListe updateList];
});
});
// release the dispatch queue
dispatch_release(jsonParsingQueue);
Sounds like you're using ARC - if _naturStream is an instance variable for an objective C class, you might need to pull it out and add a __block reference so that ARC knows the scope correctly - but I'm guessing because I don't see how the block is used with the NSInputStream (if you post that part we might know). A good bit is here: http://nachbaur.com/blog/using-gcd-and-blocks-effectively
-- edit --
Ok, now that you posted the rest, I bet it has to do with the _kortvisning and visListe variables. I think you want to pull those out right after you create your queue something like
__block KortVisning *localKortVisning = _kortvisning;
__block NSMutableArray *localVisListe = visListe;
Then access those directly from your final completion handler you're sending back to the main queue.