I am new to iPhone programming and Objective-C.
I am building a View-based application.
The problem is that none of the UIViewController's dealloc functions are ever called.
I have decided to unload all my retained objects programmaticaly, right before presenting the next UIViewController class.
I have resolved all the leaks detected by Xcode, tested the application with Leaks and Allocations Tools and everything seams OK, but the memory used builds up to around 180 MB and the app crashes.
The self.retainCount before presenting the next UIViewController is 1, the objects owned are released and the pointers are nil, but still the memory builds up.
Can you give me a clue? If needed I will post some code samples. Or can you send me some references to something like Objective-C memory management 101?
This is my interface:
#interface GameScreen : UIViewController
{
IBOutlet UILabel *timeLabel;
IBOutlet UIView *gameView;
IBOutlet UIImageView *gameViewFrame;
IBOutlet UIButton *showOutlineButton;
UIImage *puzzleImage;
UIView *optionsView;
UIView *blockView;
NSTimer *timer;
UIImageView *viewOriginalPicture;
SHKActivityIndicator *activityIndicator;
BOOL originalPictureShown;
BOOL outLineShown;
}
#property (nonatomic, retain) IBOutlet UILabel *timeLabel;
#property (nonatomic, retain) IBOutlet UIView *gameView;
#property (nonatomic, retain) IBOutlet UIImageView *gameViewFrame;
#property (nonatomic, retain) IBOutlet UIButton *showOutlineButton;
#property (nonatomic, retain) UIImage *puzzleImage;
And here is the implementation:
- (id) initWithPuzzleImage: (UIImage *) img
{
if((self = [super init]))
{
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
}
return self;
}
This is the function called when the user taps the exit button:
- (void) onExit
{
[timer invalidate];
CurrentMinuts = 0;
CurrentSeconds = 0;
//remove piece configurations
[pieceConfigMatrix removeAllObjects];
[pieceFramesMatrix removeAllObjects];
PuzzleViewController *modalView = [[PuzzleViewController alloc] init];
[self unloadObjects];
[self presentModalViewController:modalView animated:YES];
[modalView release];
}
And the unloadObjects function:
- (void) unloadObjects
{
[self resignFirstResponder];
[viewOriginalPicture release];
viewOriginalPicture = nil;
[timeLabel release];
timeLabel = nil;
[gameView release];
gameView = nil;
[originalImage release];
originalImage = nil;
[gameViewFrame release];
gameViewFrame = nil;
[timer release];
[showOutlineButton release];
showOutlineButton = nil;
}
I have a lead of what I do wrong, but I am not sure. Let me explain. I am adding the puzzle pieces to the 'gameView' property. For this, I have a 'SplitImage' object. The following function is called in - (void) viewDidLoad:
- (void) generatePuzzleWithImage:(UIImage *) image
{
SplitImage *splitSystem = [[SplitImage alloc] initWithImage:image andPuzzleSize:gPuzzleSize];
[splitSystem splitImageAndAddToView:self.gameView];
[splitSystem release];
}
Next, initialization function for the SplitImage class and the splitImageAndAddToView function:
- (id) initWithImage: (UIImage *) image andPuzzleSize: (int) pSize
{
if((self = [super init]))
{
UIImage *aux = [[[UIImage alloc] init] autorelease];
pieceCenterSize = [SplitImage puzzlePieceSizeForNumberOfPieces:pSize];
UIImage *outSideBallSample = [UIImage imageNamed:#"sampleHorizontal.jpg"]; //convexity size for puzzle size
outSideBallSample = [outSideBallSample resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(pieceCenterSize, pieceCenterSize) interpolationQuality:kCGInterpolationHigh];
outSideBallSize = roundf(outSideBallSample.size.height);
puzzleSize = pieceCenterSize * pSize;
pieceNumber = pSize;
if(image.size.height < puzzleSize || image.size.height > puzzleSize || image.size.width < puzzleSize || image.size.width > puzzleSize)
{
aux = [SplitImage resizeImageForPuzzle:image withSize:puzzleSize];
aux = [SplitImage cropImageForPuzzle:aux withSize:puzzleSize];
}
aux = [aux imageWithAlpha];
originalImage = [[UIImage imageWithCGImage:aux.CGImage] retain];
mainImage = aux.CGImage;
imageSize = CGSizeMake(aux.size.width, aux.size.height);
NSLog(#"%#", NSStringFromCGSize(imageSize));
splitImageSize = CGSizeMake(pieceCenterSize + 2*outSideBallSize, pieceCenterSize+2*outSideBallSize);
}
return self;
}
- (void) splitImageAndAddToView: (UIView *) view
{
for (int i = 0; i < pieceNumber; i++)
for(int j = 0; j < pieceNumber; j++)
//some code
UIImage *mask;
mask = [self randomlyRetriveMaskWithPrefix:1 forPieceAtI:i andJ:j];
CGImageRef split = CGImageCreateWithImageInRect(mainImage, cuttingRect);
PuzzlePiece *splitView = [[PuzzlePiece alloc] initWithImage:[UIImage imageWithCGImage:split] andMask:mask centerSize:pieceCenterSize objectMatrixPosition:i*pieceNumber+j outSideBallSize:outSideBallSize pieceType:pieceType pieceFrame:cuttingRect];
[pieceFramesMatrix addObject:[NSValue valueWithCGRect:cuttingRect]];
[splitView setTag:(i+1)*100+j];
[view addSubview:splitView];
CGImageRelease(split);
[splitView release];
}
Thank you,
Andrei
Objective-C memory management 101 is here:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Ignore the stuff on garbage collection, it isn't available for iOS.
It's not normal to release objects before presenting the next UIViewController.
Assuming you have a NIB, its contents will be loaded the first time that you (or the system) accesses the view member. Your view controller will get a call to loadView, then subsequently to viewDidLoad. If you have any views you want to add programmatically, you can do that in loadView or viewDidLoad, if you want to interrogate objects loaded from the NIB then you can do that in viewDidLoad.
Once loaded, the view will remain in memory unless and until a low memory warning occurs. At that point it'll be released. You'll get viewDidUnload. You should release anything view related that you've still got an owning reference to in there, and set to nil any weak references you may have.
Any attempt to access the view property subsequently will cause the NIB to be reloaded, etc.
So, when presenting a new UIViewController, just present it. If you create objects that are used only for display of that controller then do so on viewDidLoad, and release them on viewDidUnload.
That all being said, the Leaks tool in Instruments should be able to tell you which types of object are leaking and where you first allocated them, so it makes finding leaks really quite easy. The only thing to watch out for is that if one object handles its properties/members entirely correctly but is itself leaked then anything it creates will generally also leak. So when something leaks, check the thing that created it isn't also leaking before tearing your hair out over why you can't find a problem.
First, retainCount is useless.
Activity Monitor is pretty close to just as useless. As well, the behavior in the simulator can be quite different on the memory use front. Better to focus debugging of a problem like this on the device.
Next, this:
The problem is that none of the
UIViewController's dealloc functions
are ever called. I have decided to
unload all my retained objects
programmaticaly, right before
presenting the next UIViewController
class.
Any time you find yourself programmatically working around incorrect behavior, you are just creating more bugs.
Step back from your custom hack and figure out why the dealloc isn't being called. Something somewhere is over-retaining the object. The allocations instrument with retain tracking turned on will show you exactly where all retains and releases are sent to the errant objects.
Leaks likely won't show anything if whatever is retaining the objects is still reachable from a global or the stack (i.e. leaks are objects that can never be used by your program again -- but there are many more ways to explode memory use without it being truly a leak).
You should also "Build and Analyze", then fix any problems it identifies.
Next, if you are seeing memory accretion of repeated operations on the user's part, then Heapshot analysis is extremely effective at figure out exactly what has gone wrong.
Some specific comments:
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
2 bugs; you are leaking a UIImage and not retaining img. That your app doesn't crash in light of the above code indicates that there is likely an over-retain elsewhere.
retainCount is not a reliable debugging tool.
You should never pay attention to or rely on retainCount. Just because you released it does not mean that some part of the program does not still have a reference to it. retainCount has no value.
Generally as a rule of thumb. If you use 'alloc' you must 'release' at some point.
Unless ofcourse you have put it into an autorelease pool.
Leaks should be able to point you to the objects that are leaking, using that, narrow down to where those objects are added, stored etc.
Post examples of your code on how you instantiate objects and where you release them, you maybe doing something wrong early on.
edit: apologies, i put 'init' not 'alloc' previously, thank you dreamlax, early morning mistake.
Related
Being new to objective-C coding I started out writing a basic app, fully programmatically (not using storyboards or xib) in one file, my AppViewController h and m files.
Everything worked lovely.
So then I wanted to break up the mass of code by subclassing sections, and everything went well apart from the UIPickerView. In fact simply commenting out the [background addSubview:colorPicker]; seemed to totally fix the issue. I never found the answer online so I proceeded to make a new document to replicate said issue.
So here goes:
UIPickerViewController.h
#import <UIKit/UIKit.h>
#import "Picker.h"
#interface UIPickerViewController : UIViewController
#end
Simply imports my new class.
UIPickerViewController.m
#import "UIPickerViewController.h"
#interface UIPickerViewController ()
#end
#implementation UIPickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *superview = self.view;
int height = superview.bounds.size.height;
int width = superview.bounds.size.width;
CGRect popupRect = CGRectMake(0, 0, width, height);
UIView *popup = [[UIView alloc]initWithFrame:popupRect];
popup.tag = 8;
[superview addSubview:popup];
Picker *picker = [[Picker alloc]initWithFrame:popupRect];
[picker viewAddTypeScreenToView:superview];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Sets up a new view with a tag (so that i could reference it later with my new class)
Then actions a method from my new class to populate my new view.
Picker.h
#import <UIKit/UIKit.h>
#interface Picker : UIView
<UIPickerViewDataSource,UIPickerViewDelegate>
{
UIPickerView *colorPicker;
NSMutableArray *colorsArray;
}
#property (nonatomic, retain) UIPickerView *colorPicker;
#property (nonatomic, retain) NSMutableArray *colorsArray;
#property (strong,nonatomic) UILabel *myValue;
-(void)viewAddTypeScreenToView:(UIView*)superview;
#end
Setting up my variables and accessible method.
Picker.m
#import "Picker.h"
#implementation Picker
#synthesize colorsArray;
#synthesize colorPicker;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)viewAddTypeScreenToView:(UIView*)superview
{
UIView *baseView =[superview viewWithTag:8];
int height = baseView.bounds.size.height;
int width = baseView.bounds.size.width;
CGRect fullScreen = CGRectMake(0, 0, width, height);
UIView *background = [[UIView alloc]initWithFrame:fullScreen];
background.backgroundColor = [UIColor blackColor];
colorsArray = [[NSMutableArray alloc] initWithObjects:#"Red",#"Blue",#"Yellow",#"Green",nil];
CGRect myPickerRect = CGRectMake(10, 70, (width/2)-40, 200);
colorPicker = [[UIPickerView alloc]initWithFrame:myPickerRect];
colorPicker.dataSource = self;
colorPicker.delegate = self;
colorPicker.showsSelectionIndicator = YES;
[colorPicker selectRow:2 inComponent:0 animated:YES];
CGRect labelFrame = CGRectMake(10, 10, 180, 50);
_myValue = [[UILabel alloc]initWithFrame:labelFrame];
_myValue.textColor = [UIColor redColor];
_myValue.text = #"select colour";
[background addSubview:_myValue];
[background addSubview:colorPicker];
[baseView addSubview:background];
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return colorsArray.count;;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return colorsArray[row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
_myValue.text = [NSString stringWithString:colorsArray[row]];
}
#end
And finally the initiation called by the method in the picker class file.
This gives me an error along these lines
-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000
2014-03-19 10:29:48.407 Briefcase[1800:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000'
Which i've read is to do with either the datasource, or ARC systems, however none of the responses that I have found relate to or work with the type of set up that I have above. I'm sure it's something really simple but after a few days of failed searching, it's officially driving me crazy.
The problem is most likely that the instance of Picker that is being created in UIPickerViewController is never added to the view hierarchy and thus gets released prematurely (provided we're talking about a project using ARC here).
This leads to the pickerview's delegate and datasource becoming invalid and, basically, pointing at any random object. That's what is causing your crash: A message to your delegate cannot be delivered because the delegate is dead already. The picker still keeps a pointer which used to point at the delegate, but which has become invalid and points at a random object now, in this case a table view cell, which basically doesn't know what to do with this message and crashes.
The problem should go away if you add Picker *picker as an ivar or a retaining / strong property to UIPickerViewController.h - this will retain the picker beyond the scope of the viewDidLoad method and should keep it alive.
But that would be just a workaround, the real problem is your overall design. You said you're new to objective-c and indeed, it looks like you lack a basic understanding of iOS view and view controller hierarchies and, to some degree, the concept of object oriented programming. You might want to dig into something more basic before trying to fix your code because, quite frankly, it should be rather re-written than fixed.
I'd be happy to provide you with suggestions about how to structure your code, but please provide some information about what functionality you'd like to achieve first.
Edit (in response to your comment):
As a rule of thumb, do not spread functionality over several classes unless necessary. For objects, which serve a rather infrastructural purpose, like a specialized textfield or a pickerview, always ask yourself: "If I would like to reuse that object in another project, would that be as easy as using any other existing object, like, for example, UILabel?" If the answer is "No", then something is wrong. Ideally, interface objects are self-contained and to use them, you just invoke them, add them to a view and tell them, which text to display or which options to offer. If that information is subject to change or if the object needs to interact with other parts of your code, make use of delegation and protocols. Under no circumstances should the functionality of your object be tied to hard coded values or rely to some view to have a certain tag.
If you subclass UIView, the resulting object should behave like any other instance of UIView. It should be added to the view hierarchy by you or some object, but it shouldn't add or remove itself. If it works without being added to the view hierarchy at all, something is wrong. A view serves the purpose of being a part in your interface and all the logic it contains should work to that end, not more, not less.
Normally, interface objects should not interfere with one another. If something happens to one object (button pressed, option selected, text changed...) and another object is supposed to reflect that change, it is the view controllers responsibility to make that happen. The view controller is the place where the logic happens. If there is a task which requires a lot of complex logic, it might be a good idea to encapsule that logic into a purpose build class. One such example would be a class which manages network connections. This class should be again self contained: If the view controller needs some remote information, it asks your network class. Once your network class has that information (or failed to retrieve it), it reports back to your view controller. The view controller then updates the interface - under no circumstance should the networking class contain code which affects the interface.
It is important to understand that you could very well ignore these rules and still end up with a working app. And in some cases, the "direct" way may appear to be easier to implement and thus may look very tempting. But you'll pay the price later - once you start debugging your code. If your picker does not behave the way it should, you need to look into several places and wrap your mind around several objects, just to make one interface object behave right. And likely you will break one functionality while fixing the other.
So, try to make it right from the start, even though it requires more planning and learning. Trust me, it pays out, I started out just like you several years ago ;)
I've wrote a class which gets an image from the camera. Its header is as follows:
typedef void(^ImageTakenCallback)(UIImage *image);
#interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
UIImagePickerController *picker;
ImageTakenCallback completionBlock
}
-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;
#end
As you can see, I'm trying to make something like that in client code:
[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
}];
Here is how I've implemented ImageGetter:
-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
completionBlock = [completion copy];
picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.delegate = self;
[view addSubview:picker.view];
}
- (void)imagePickerController:(UIImagePickerController *)picker_
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
[picker.view removeFromSuperview];
picker = nil;
completionBlock(image);
}
The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.
Which are common ways to resolve such a issue?
I can see some ways, however, none of them seems to be quite right:
retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?
So how can I build such a class?
You can handle that within the ImageGetter class by creating and releasing a "self-reference".
In a class extension in the implementation file, declare a property
#interface ImageGetter ()
#property (strong, nonatomic) id selfRef;
#end
In requestImageInView:, set self.selfRef = self to prevent deallocation.
In the completion method, set self.selfRef = nil.
Remark: Actually you can manage the retain count even with ARC:
CFRetain((__bridge CFTypeRef)(self)); // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self)); // Decreases the retain count.
But I am not sure if this is considered "good programming" with ARC or not.
Any feedback is welcome!
If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.
However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.
You can use the following strategy:
ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
[imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];
Inside your ImageGetter implementation class, create a method that you can call inside the block like this.
-(void) releaseCompletionBlock
{
completionBlock = nil;
}
I have a small problem with ARC and dealloc of the BaseViewController class being called after the instantiation inside the loop and I don't know why. What I'm trying to do is basically store all the base view controllers on an array.
#interface CategoriesContainerViewController ()
#property (nonatomic, strong) IBOutlet UIScrollView* scrollView;
#property (nonatomic, strong) NSMutableArray* categoriesViews;
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Get the categories from a plist
NSString* path = [[NSBundle mainBundle] pathForResource:#"categories" ofType:#"plist"];
NSDictionary* dict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSMutableArray* categories = [dict objectForKey:#"Categories"];
NSLog(#"%i", [categories count]);
// Setup the scrollview
_scrollView.delegate = self;
_scrollView.directionalLockEnabled = YES;
_scrollView.alwaysBounceVertical = YES;
_scrollView.scrollEnabled = YES;
CGRect screenRect = [[UIScreen mainScreen] bounds];
// Loop through the categories and create a BaseViewController for each one and
// store it in an array
for (int i = 0; i < [categories count]; i++) {
BaseViewController* categoryView = [[BaseViewController alloc]
initWithCategory:[categories objectAtIndex:i]];
CGRect frame = categoryView.view.frame;
frame.origin.y = screenRect.size.height * i;
categoryView.view.frame = frame;
[_scrollView addSubview:categoryView.view];
[_categoriesViews addObject:categoryView];
}
}
You are committing a common beginner mistake by keeping a reference to a view controller's view, but not the view controller itself.
You create a BaseViewController object in a local variable categoryView. That's a strong reference, so the object is kept around. Then the loop repeats, and you create a new BaseViewController, replacing the old value in categoryView. When you do that, there are no longer any strong references to the previous BaseViewController that was in categoryView, so it gets deallocated.
If you want the BaseViewController to stick around, you need to keep a strong reference to it somewhere.
In addition to that, you are breaking another rule of iOS development. You should never put one view controller's view(s) inside another view controller's unless you use the parent/child view controller support that was added in iOS 5 and extended in iOS 6. The docs say do NOT do that.
Mixing views from multiple view controllers on the screen will cause you no end of problems. There is tons of housekeeping you have to do in order to make it work, and not all of that housekeeping is documented. Its possible, but it will take you many weeks to iron out the bugs, if you ever able to. Plus, since you are doing something that Apple expressly says not to do, the burden is on you to make it work correctly, and there is a substantial risk that a new iOS release will break your app.
Initialize BaseViewController above for loop and then store the array value inside the object of BaseViewController. Because every time it is allocating and initializing. So setting the previous object to nil. Hence the issue causes to be deallocated.
I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.
in my Mac iPhone simulator (iPhone simulator 4.0) i run my app without problem, but when i change it to another Mac which runs iPhone simulator 4.2 it crushes on a button click somewhere in the app, could the version of the debugger (4.0 and 4.2) be the cause of a potential crash ? thx in advance :))
EDIT
this is the code i have doubt it may cause crash 'cause i use in it a UIPickerView code :
- (void)viewDidLoad {
((UILabel *)[[[[[[switchCommerce subviews] lastObject] subviews] objectAtIndex:2] subviews] objectAtIndex:0]).text = #"oui";
((UILabel *)[[[[[[switchCommerce subviews] lastObject] subviews] objectAtIndex:2] subviews] objectAtIndex:1]).text = #"non";
((UILabel *)[[[[[[switchStationDeLavage subviews] lastObject] subviews] objectAtIndex:2] subviews] objectAtIndex:0]).text = #"oui";
((UILabel *)[[[[[[switchStationDeLavage subviews] lastObject] subviews] objectAtIndex:2] subviews] objectAtIndex:1]).text = #"non";
[rayonDeRechercheSlider setValue:35 animated:YES];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
CGAffineTransform transform=CGAffineTransformMakeTranslation(0,480);
pickerViewTypeCarburant.transform=transform;
pickerViewNomStation.transform=transform;
[UIView commitAnimations];
[super viewDidLoad];
typeCarburantTextField.delegate=self;
nomDeStationTextField.delegate=self;
pickerArrayTypeCarburant=[[NSMutableArray alloc] initWithObjects:#"Sans Plomb 95",#"Gazole",#"Super Gazole",#"GNV",#"Sans Plomb 98",#"SP95-E10",#"E85",nil];
pickerArrayNomStation=[[NSMutableArray alloc] initWithObjects:#"TOTAL",#"SHELL",#"ESSO",#"CARREFOUR",#"BP",#"AGIP",#"CASINO",#"LECLERC",#"AUCHAN",#"ELF",#"AVIA",#"SUPER U",nil];//added
}
One potential problem in your code is that you appear to be assigning to your mutable array properties (pickerArrayTypeCarburant & pickerArrayNomStation) without using the self. notation. What this means is that the arrays are being assigned without using retain semantics (presuming that your #property declaration for the arrays is told to use retain).
The effect of this is that when you try to access this array later in your code you will find that the array has not been retained, and you will likely be receiving some kind of memory warning. Try this instead:
self.pickerArrayTypeCarburant=[[NSMutableArray alloc] initWithObjects:#"Sans Plomb 95",#"Gazole",#"Super Gazole",#"GNV",#"Sans Plomb 98",#"SP95-E10",#"E85",nil];
self.pickerArrayNomStation=[[NSMutableArray alloc] initWithObjects:#"TOTAL",#"SHELL",#"ESSO",#"CARREFOUR",#"BP",#"AGIP",#"CASINO",#"LECLERC",#"AUCHAN",#"ELF",#"AVIA",#"SUPER U",nil];
It's hard to tell if that will solve your problem since I don't know what error is being thrown, however if it is memory related then that could be the culprit.
To be sure, you could run your app in Instruments and see if there are any leaks or allocations where there shouldn't be.
EDIT:
In response to your comment regarding properties, your header file should probably declare your NSMutableArrays like this:
YourClass.h
#interface YourClass {
NSMutableArray *pickerArrayTypeCarburant;
NSMutableArray *pickerArrayNomStation;
}
#property (nonatomic, retain) NSMutableArray *pickerArrayTypeCarburant;
#property (nonatomic, retain) NSMutableArray *pickerArrayNomStation;
// Any method names here...
#end
And at the top of your .m file, just underneath the #implementation line:
#synthesize pickerArrayTypeCarburant, pickerArrayNomStation;
The #synthesize part will automatically create the required accessors and mutators for your properties, based on the information you provide on the lines with #property. In this case, I have defined that the properties should use retain semantics, which means that when you assign a value to your property using self. (i.e. self.pickerArrayNomStation = anArray;) the array you assign to it will automatically be retained by your property. Make sure you include the self. when assigning to a property that uses retain, as this is what tells it to use the generated mutator method, rather than just assigning to the instance variable without retaining.
By the way, make sure that when you generate retained properties in this way that you call [myProperty release]; in the dealloc method, and (assuming your class is a view controller...) self.myProperty = nil in your viewDidUnload method. This ensures that any retained objects are released properly when your views and view controllers are no longer needed.
If you need further information about this stuff, Apple's documentation is your friend. I found their programming guides really helpful when I was starting out. The guide I linked you in my comment under your answer is a good one to start with.
Hope this helps.
EDIT 2
Not sure if this will help, but I have sometimes found that assigning arrays in this way is a little safer... at the bottom of the ViewDidLoad method where you are assigning to your array property, change the code to this:
NSMutableArray *newArrayTypeCarburant = [[NSMutableArray alloc] initWithObjects:#"Sans Plomb 95",#"Gazole",#"Super Gazole",#"GNV",#"Sans Plomb 98",#"SP95-E10",#"E85",nil];
NSMutableArray *newArrayNomStation = [[NSMutableArray alloc] initWithObjects:#"TOTAL",#"SHELL",#"ESSO",#"CARREFOUR",#"BP",#"AGIP",#"CASINO",#"LECLERC",#"AUCHAN",#"ELF",#"AVIA",#"SUPER U",nil];
self.pickerArrayTypeCarburant = newArrayTypeCarburant;
self.pickerArrayNomStation = newArrayNomStation;
[newArrayTypeCarburant release];
[newArrayNomStation release];