I have a program which works normally. Then I downloaded some code from http://github.com/matej/MBProgressHUD to show a progress meter when doing something.
This is the code that makes the progress meter pop up.
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
This will show a progress meter while the method myTask is running.
This is the code for the showWhileExecuting method.
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
methodForExecution = method;
targetForExecution = [target retain];
objectForExecution = [object retain];
// Launch execution in new thread
taskInProgress = YES;
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
If I use this to call the function myTask then one of my class properties will change from an NSMutableString to an NSData object somewhere, and then later on it will change to an NSString. I don't see anywhere in the code that causes this to change, so it's probably some kind of bug. Is memory getting corrupted? What's causing this to happen?
Most likely it's a memory (retain/release issue). If you don't properly retain an object, it may get released out from under you. At that point, the memory will be reclaimed by the OS, which may decide to store something else there. Try turning on NSZombies, and double checking your retain/release/autoreleases.
Related
I see a crash in Crashlytics than happens to my users sometimes. The crash happens when presenting UIActivityViewController in the last line of the following code:
NSData* snapShot = ... ;
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:[NSArray arrayWithObjects:activityTextsProvider, snapShot ,nil] applicationActivities:[NSArray arrayWithObjects:customActivityA, customActivityB, customActivityC, nullptr]];
activityViewController.excludedActivityTypes = [NSArray arrayWithObjects:UIActivityTypePrint, UIActivityTypeAssignToContact, UIActivityTypeMail, UIActivityTypeCopyToPasteboard, nil];
activityViewController.popoverPresentationController.sourceView = self.myButton;
activityViewController.popoverPresentationController.sourceRect = self.myButton.bounds;
activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError)
{
...
};
[self presentViewController:activityViewController animated:YES completion:nil];
I perform this in the main thread and unable to reproduce this crash locally. What could be the reason of this crash?
Edit: I changed nullptr to nil and the issue still happened. I managed to reproduce the issue: the crash happens only if before opening the activity controller i showed a UIMenuController. When creating UIActivityViewController it is not nil, but when presenting the controller i see the crash in the presentViewController line and the activity controller there is shown as nil
József addressed the use of nullptr in comments, and Fogh is spot-on that the actual crash log is important (please edit your question and post the full crash log), but I'd like to point out something else.
You're assuming your call to initialize activityViewController is succeeding. You should code defensively (by assuming everything that can fail probably will fail and testing for this at runtime). Wrap the rest of the configuration and presentation inside an if (activityViewController != nil) {} condition (you should probably have an else with proper error handling/reporting too) so you're properly detecting an all-out initialization failure for multiple reasons (like a misplaced nib, missing resource, etc.).
In your case, I think it's likely the initialization is failing because your class is doing something with a faulty array, as József's nullptr catch suggests. Perhaps you're using one or more pre-C++11 c libraries / compiling with a non-C11/gnu11 "C Language Dialect" build setting and nullptr is not equivalent to nil, leading to strange results in a supposed-to-be-nil-terminated array?
Note: If that turns out to be the case, I'll happily take an upvote but would rather József post his comment as an answer so you can give him proper credit. (Feel free to edit this request out of my answer if/when that happens.)
Currently i'm having trouble using two functions in my -(void)viewDidLoad, both of these functions uses NSUrlRequest to send HTTPPost to a webservice to recieve data.
It works fine untill [self bar] decides to kick in before [self foo] is completely finished. So, is there any smart way of checking if [self bar] is completely finished before starting [self foo]?
-(void)viewDidLoad{
[self foo]; // initiates a nsxmlparsercall to a webservice to get values.
[self bar]; // relies on the values recieved from [self foo] to make it's own call.
/* However, [self bar] always crashes before initiating it's request.
/* It crashes when the variables that are being sent with the poststring
/* are being set, as they are null.
/* Which means that the `[self foo]` doesnt get completed before starting [self bar];
}
I might be very off at this point, i've even considered overriding -(void)viewDidload and setting a bool to control when it's ok to fire the second function, but that seems like super poor coding..
Any suggestions and/or tips on how to point me in the right direction will be highly appreciated. Thanks in advance.
I best place to put your function will be one of the delegate methods of nsxmlparser that is
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[self bar];
}
This fine if you are parsing the response on a background thread and it doesn't matter if the function bar is called on main thread or background thread.
But if you want to call the bar function specifically on main thread then you can use this function
[self performSelectorOnMainThread:#SEL(bar) withObject:nil waitUntilDone:YES];
you mean in [self foo] function you want to parse some thing and when its completely parsed then you want to call [self bar]; function right?
okay then you can fire a notification when parsing gets completed. in by this notification you can call the method you want.
I have a memory leak in my iPhone app. I added Google AdMob to my app using sample code downloaded from Google. However, I was having a hard time getting it into testing mode so I added an extra variable as follows:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
I found the memory leak using Instruments. Instruments does not lead me to this line of code, it just dumps me in the main.m file. However, when I comment out the code relating to AdMob the leak goes away and I know enough to see that I haven't taken care of releasing this new variable. I just don't know exactly how I should set about releasing it. The variable r is not addressed in the header file so this is all the code that deals with it.
I tried adding:
- (void)dealloc {
[r release];
....
}
but that caused a build error saying "'r' undeclared". That's weird because it looks to me like I'm declaring r in the first quoted line above but I guess that's wrong. Any help would be much appreciated. I really have tried to educate myself about memory leaks but I still find them very confusing.
Just add [r release]; right below the code:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release];
The variable r is declared only in that section of your code, so that's where it should be released. The point of releasing is to get rid of it as soon as you no longer need it, so the above should work perfectly for you.
If your r is locally declared (as it seems, judging from your snippet), then it cannot be accessed from outside its scope (here: the method it was declared in).
You either need to make it accessible within your class instance by declaring it an ivar.
Declaring it an ivar would look like this:
#interface YourClass : SuperClass {
GADRequest *request;
}
//...
#end
You then change your code to this:
request = [[GADRequest alloc] init];
request.testing = YES;
[bannerView_ loadRequest:request];
Also don't forget to release it in dealloc:
- (void)dealloc {
[request release];
//...
}
However this is not what you want in this situation (I've just included it to clarify why you get the warning about r not being declared).
You (most likely) won't need request any second time after your snippet has run, thus storing it in an ivar will only needlessly occupy RAM and add unwantd complexity to your class. Stuff you only need at the immediate time after its creation should be taken care of (released) accordingly, that is within the very same scope.
What you'll actually want to do is simply (auto)release it, properly taking care of it.
Keep in mind though that your loadRequest: will need to take care of retaining r for as long as it needs it. Apple's implementation does this, of course. But you might want to write a similar method on your own one day, so keep this in mind then.
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release]; //or: [r autorelease];
OP here. Thanks for all the detailed and thoughtful responses. This has definitely helped get a better handle on memory management. I did exactly as recommended, adding [r release]; right below the posted code. However, I still have a leak. I have isolated it to a single line of code. The following leaks:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release];
The following does not leak:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
// [bannerView_ loadRequest:r];
[r release];
I figure I'm changing the retain count on bannerView with the loadRequest but I don't know how to fix it. I tried a [bannerView_ release] immediately after the [r release]; line (i.e. releasing locally) but that didn't work. I didn't expect it to because bannerView_ is declared elsewhere. I tried [bannerView_ release]; in the dealloc method but that didn't work. I also tried [bannerView_ autorelease]; locally. The wise heads at Google put [bannerView_ release]; in the ViewDidUnload method.
It's possible that Instruments is just messing with my head. The leak appears after about 10 seconds but the app performs well and the amount of memory leaked doesn't seem to be spirally upwards as the app continues to run. Is there such a thing as a benign memory leak?
Thanks again for your help,
Dessie.
I'm trying to get data from a website- xml. Everything works fine.
But the UIButton remains pressed until the xml data is returned and thus if theres a problem with the internet service, it can't be corrected and the app is virtually unusable.
here are the calls:
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.XMLdataArray.count > 0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray.
}
XMLViewController *controller = [[XMLViewController alloc] initWithNibName:#"MedGearsApps" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
It works fine, but how can I make the view buttons functional with getting stuck. In other words, I just want the UIButton and other UIButtons to be functional whiles the thing works in the background.
I heard about performSelectorInMainThread but I can't put it to practice correctly.
You don’t understand the threading model much and you’re probably going to shoot yourself in the foot if you start adding asynchronous code without really understanding what’s going on.
The code you wrote runs in the main application thread. But when you think about it, you don’t have to write no main function — you just implement the application delegate and the event callbacks (such as touch handlers) and somehow they run automatically when the time comes. This is not a magic, this is simply a Cocoa object called a Run Loop.
Run Loop is an object that receives all events, processes timers (as in NSTimer) and runs your code. Which means that when you, for example, do something when the user taps a button, the call tree looks a bit like this:
main thread running
main run loop
// fire timers
// receive events — aha, here we have an event, let’s call the handler
view::touchesBegan…
// use tapped some button, let’s fire the callback
someButton::touchUpInside
yourCode
Now yourCode does what you want to do and the Run Loop continues running. But when your code takes too long to finish, such as in your case, the Run Loop has to wait and therefore the events will not get processed until your code finishes. This is what you see in your application.
To solve the situation you have to run the long operation in another thread. This is not very hard, but you’ll have to think of a few potential problems nevertheless. Running in another thread can be as easy as calling performSelectorInBackground:
[appDelegate performSelectorInBackground:#selector(GetApps) withObject:nil];
And now you have to think of a way to tell the application the data has been loaded, such as using a notification or calling a selector on the main thread. By the way: storing the data in the application delegate (or even using the application delegate for loading the data) is not very elegant solution, but that’s another story.
If you do choose the performSelectorInBackground solution, take a look at a related question about memory management in secondary threads. You’ll need your own autorelease pool so that you won’t leak autoreleased objects.
Updating the answer after some time – nowadays it’s usually best to run the code in background using Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
[self doSomeLongOperation];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
[self longOperationDone];
});
});
Use NSURLConnection's connectionWithRequest:delegate: method. This will cause the specified request to be sent asynchronously. The delegate should respond to connection:didReceiveResponse: and will be sent that message once the response is completely received.
You can make use of a background operation that gets pushed into the operation queue:
BGOperation *op = [[BGOperation alloc] init];
[[self operationQueue] addOperation:op];
[op release];
I've created specific "commands" that get executed in the background:
#implementation BGOperation
# pragma mark Memory Management
- (BGOperation *)init
{
if ((self = [super init]) != nil)
/* nothing */;
return self;
}
- (void)dealloc
{
self.jobId = nil;
[super dealloc];
}
# pragma mark -
# pragma mark Background Operation
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[appDelegate GetApps];
[pool release];
return;
}
#end
After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.
It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.
I have been trying to get this one section of my UI to immediatly up date when the document loads into view. The awakeFromNib fires the pasted code and then starts a timer to repeat every 10 seconds...
I load a default storage location: ~/Movies... which shows up immediately.. yet the network location that is saved in the document that gets pulled from the XML only seems to show up after the second firing of the - (void)updateDiskSpaceDisplay timer.
I have set breakpoints and know that the ivars that contain the values that are being put into the *fileSystemAttributes is the network location right when the awakeFromNib occurs...
Im confused why it magically appears after the second time firing instead of immediately displaying the write values.
- (void)updateDiskSpaceDisplay
{
// Obtain information about the file system used on the selected storage path.
NSError *error = NULL;
NSDictionary *fileSystemAttributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[[[self settings] containerSettings] storagePath] error:&error];
if( !fileSystemAttributes ) return;
// Get the byte capacity of the drive.
long long byteCapacity = [[fileSystemAttributes objectForKey:NSFileSystemSize] unsignedLongLongValue];
// Get the number of free bytes.
long long freeBytes = [[fileSystemAttributes objectForKey:NSFileSystemFreeSize] unsignedLongLongValue];
// Update the level indicator, and the text fields to show the current information.
[totalDiskSpaceField setStringValue:[self formattedStringFromByteCount:byteCapacity]];
[totalDiskSpaceField setNeedsDisplay:YES];
[usedDiskSpaceField setStringValue:[self formattedStringFromByteCount:(byteCapacity - freeBytes)]];
[usedDiskSpaceField setNeedsDisplay:YES];
[diskSpaceIndicator setMaxValue:100];
[diskSpaceIndicator setIntValue:(((float) (byteCapacity - freeBytes) / (float) byteCapacity) * 100.0)];
[diskSpaceIndicator display:YES];
}
thoughts?
my awakeFromNib:
- (void)awakeFromNib
{
[documentWindow setAcceptsMouseMovedEvents:YES];
[documentWindow setDelegate:self];
[self updateSettingsDisplay];
[self updateDiskSpaceDisplay];
[self setDiskSpaceUpdateTimer:[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(updateDiskSpaceDisplay) userInfo:NULL repeats:YES]];
[self setUpClipInfoTabButtons];
[self performSelector:#selector(setupEngineController) withObject:NULL afterDelay:0.1];
}
awakeFromNib is only a notification to your instance that its IBOutlets have been set up, not that the application has finished launching and is ready to draw.
Try implementing NSApplication's delegate method, applicationDidFinishLaunching:, and call your updateDiskSpaceDisplay method from there.
there was an read from xml method that was occurring after my awakeFromNib that was swizzling the values... I just put the updateDiskSpaceDisplay there and... problem solved. Thanks all for the attempts.