Xcode 4 static code analysis question - ios4

This is the follow up to my question earlier about the Xcode 4 static analyzer. It is not specifically a problem since I have the code now working as it needs to, but I am just wondering how things are working behind the scenes. Consider the following code:
- (IBAction)cameraButtonPressed:(id)sender
{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == NO)
{
return;
}
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.allowsEditing = NO;
cameraUI.delegate = self;
[self presentModalViewController:cameraUI animated:YES];
NSString *theString = [[NSString alloc] initWithString:#"cameraButtonPressed done"];
NSLog(#"%#", theString);
}
To me, the way this code looks, there are two objects (cameraUI and theString) that need to be released. However, the analyze function correctly identifies that only theString needs to be released at the end of the method, even though both objects are returned from alloc-init, which in my experience has always meant that you release when you are done.
The question I have here is, how does the static code analyzer know not to flag cameraUI as an issue?

I would call this a bug with the static analyzer. The UIImagePickerController instance assigned to cameraUI should be released or autoreleased in a non-garbage-collected environment (like iOS).

Related

Should NSFetchRequest be explicitly performed on context queue?

Just a little question...
When performing CoreData requests (in objective-C), do I have to make them on the context queue explicitly, or are they assumed to do that by themselves?
In other words can I just write:
NSArray *results = [context executeFetchRequest:request error:nil];
Or do I have to write (if I want to avoid issues):
__block NSArray *results = nil;
[context performBlockAndWait:^{
results = [context executeFetchRequest:request error:nil];
}];
Thanks!
You can never assume that Core Data will use the correct queue. You always need to tell it via one of the “perform” methods. The only exception is if you have a main-queue context and you know your code is running on the main queue. You can skip “perform” in that one and only case.
You mention using a convenience method to do a fetch, using “perform” inside the method. That’s fine, but don’t forget that you also need to use “perform” any time you use any of the fetched objects. For example, you can’t use property values from fetched objects unless you also use “perform” (again with the one exception I mentioned).
So just to be sure Tom:
All my CoreData objects (subclasses of NSManagedObject) have their CoreData properties as private (with a prefix "cd_"). I never expose them and use wrapper properties instead:
- (nullable NSString *) email {
return self.cd_email;
}
I've created a method on NSManagedObject:
- (id) safeValueForProperty: (SEL)property {
__block id value = nil;
[self.managedObjectContext performBlockAndWait:^{
value = [self valueForKey:NSStringFromSelector(property)];
}];
return value;
}
So now I can use (from any thread in theory):
- (nullable NSString *) email {
return [self safeValueForProperty:#selector(cd_email)];
}
(and of course the same for setters).
Am I doing right?

NSURLSession and Connection terrible memory leaks

every time i make a request to my web server via NSURLSession/Connection it leaks! To be honest it is not much but if you have to make a couple hundreds or thousands of calls this gets nasty.
I have been on this for about a week now. I have tried everything. NO cache , little cache, setting everything on nil after the call is done(which is unnecessary), using datatask for sessions or just connection with requests. Every time i get a little more memory allocated and i have not found a way to solve this problem.
So i set up a little testApp:
#import "ViewController.h"
#interface ViewController ()
#property NSString *param;
#property NSURL * url;
#property NSURLConnection *test;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.param = [NSString stringWithFormat:#"hi"];
self.url = [[NSURL alloc]initWithString:#"http://127.0.0.1/xmlfile.php"];
for (int i = 0; i<20000; i++){
[self connect:self.param url:self.url];
}
NSLog(#"I AM DONE!");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSData *)connect:(NSString*)param url:(NSURL*)url{
self.test = [[NSURLConnection alloc]initWithRequest:nil delegate:self];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
return nil;
}
#end
i would love to add images of the memory usage but i am too new.
so just go here:
5k:iterations
http://i59.tinypic.com/123tts2.png
20k:
http://i59.tinypic.com/1zzqsrk.png
I have heard that this could be a problem with ios8.
Please help!
I am open for everything and would be happy if someone could prove me wrong and show me the right way. Thanks a bunch
You're never actually turning your URL into a request and giving it to the NSURLConnection. Because of that, the connection will never complete, and its memory will stay allocated. You'll find a world of difference with the above code if you add this to your connect:url: method:
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url];
...and pass that into your NSURLConnection so it actually does something:
self.test = [[NSURLConnection alloc]initWithRequest:request delegate:self];
...because then, as soon as the connection finishes doing its work—successfully or otherwise—its memory will be deallocated.

MKUserTrackingBarButtonItem not releasing mapViewController

Just came across a bug in MapKit, wanted to see what the community is experiencing. When adding a MKUserTrackingBarButtonItem to a UIToolBar, I'm seeing the map is not releasing from memory when switching to another viewController (though I see the dealloc is firing).
Can anyone confirm they are seeing this behavior? My testing shows the map releases properly if I do not add MKUserTrackingBarButtonItem. Using iOS7, testing with Instruments/Leaks.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
MKUserTrackingBarButtonItem *userTrackingBarButtonItem = [[MKUserTrackingBarButtonItem alloc] initWithMapView:self.mapView];
[userTrackingBarButtonItem setAction:#selector(track:)];
UIBarButtonItem *flexibleSpaceBarButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[self setToolbarItems:[NSArray arrayWithObjects:flexibleSpaceBarButton, userTrackingBarButtonItem, flexibleSpaceBarButton, nil] animated:YES];
[self.navigationController setToolbarHidden:NO animated:NO];
}
MKUserTrackingBarButtonItem and the MKMapView each hold strong references to each other, resulting in a circular retain. This appears to be a bug in MapKit itself, and it's still present in iOS 7.1.
A solution is to add a dealloc to your view controller that contains the MapView:
-(void)dealloc
{
userTrackingBarButtonItem.mapView = nil; // Circular reference bug workaround
}

EXC_BAD_ACCESS Mystery

I could use a little help on this one. I narrowed the crash source down to NSMutableArray viewControllers by commenting out the release statement in dealloc.
Code:
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < self.numSlides; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
// Later on in dealloc:
[viewControllers release], viewControllers = nil;
In the main.m file [ int retVal = UIApplicationMain(argc, argv, nil, nil); ] we get:
Program received signal: "EXC_BAD_ACCESS"
When I comment out the release for viewControllers -> no crash but memory leak.
Are you out there Obi-Wan - you are our last hope.
Thanks in advance for the help.
The code you posted looks fine; without seeing any of the rest of it, I’d guess that your viewControllers property is declared as assign rather than retain. If that isn’t it, try running the static analyzer (Product > Analyze or Cmd-Shift-B); it can usually catch that kind of problem.

How to release a AVAudioPlayer object when it has to play a short sound every second after pressing a button

I have the following problem making me crazy.
My app has to play a tick sound every second for a specified number of times (e.g. 5) after the user has pressed a button.
I used this:
for (int w=1; w<=5; w++) {
[NSThread detachNewThreadSelector:#selector(tic) toTarget:self withObject:nil];
[NSThread sleepForTimeInterval:1.0];
}
where:
- (void)tic {
NSAutoreleasePool *ticPool = [[NSAutoreleasePool alloc] init];
player_tic = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"tick" ofType:#"aif"]] error:nil];
[player_tic setDelegate:self];
[player_tic play];
[ticPool drain];
}
and:
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *)player_tic successfully:(BOOL)flag {
NSLog(#"Audio finished playing.");
[player_tic release];
}
All seems to work. My problem is that the object are released together at the end.
I need that after every played sound the related 'player_tic' has to be released immediately.
Other Info:
In .m:
#synthesize player_tic;
In .h:
#interface myController : UIViewController {
...
AVAudioPlayer *player_tic;
}
#property (nonatomic, retain) AVAudioPlayer *player_tic;
On top of all, I have this warning in compilation:
local declaration of 'player_tic' hides instance variable
Please help me!
Thank you very much in advance.
--Carlo
I found System Sound Services more responsive than AVAudioPlayer.
It also solves your release problem, which is caused by the retain property of your controller. Read the reference manual on memory management for further information on retain/release.
I think you want to use self.player_tic when setting that variable, and i believe you also want to autorelease that instance. I'm not sure that setting up a separate NSAutoreleasePool is necessary either.
self.player_tic = [[[AVAudioPlayer alloc] initWithContentsOfURL:... error:nil] autorelease]

Resources