Is this release call causing instruments to crash? - iphone

I have a class called CategoryViewController and its viewDidLoad method calls this method:
- (void)reset {
Category * c = [[Category alloc] initWithId:0 title:#"Categories"];
[self setCategory:c];
[c release]; // <--- line of code I am interested in
self.title = #"Categories";
[self fillCategory];
}
In most situations category here would be null, but sometimes reset needs to be called after category has been assigned. Without the line I marked in my code, the program builds and debugs just fine and I can pull up instruments and check my leaks. The only leak I can find is a category initialized from this function (because without the release, I get a leak when calling this function on a CategoryViewController that has already been initialized).
If I try to run this method as is WITH the release on c, Instruments, XCode, and the Simulator all begin to act strangely, crashing and freezing, giving me random SIGABRTs and SIGKILLs. I can build and debug with the line of code in there, but Instruments won't even start my app. Can anyone give me a hint as to what's going on here?
EDIT: More code
#implementation Category
#synthesize title, articleCount, seeAlso, categoryId, articles, subcategories;
- (id)initWithId:(NSInteger)cid title:(NSString*)t{
self.title = t;
self.categoryId = cid;
[self setArticles:[[NSMutableArray alloc] init]];
[self setSubcategories:[[NSMutableArray alloc] init]];
[self setSeeAlso:[[NSMutableArray alloc] init]];
self.articleCount = 0;
return self;
}

It's funny how these things seem to be resolved so easily after you take the time to post them online. After posting the Category init code I realized I wasn't properly releasing the allocations I made. My leaks as well as my crashes appear to be gone after proper memory management like so:
- (id)initWithId:(NSInteger)cid title:(NSString*)t{
self.title = t;
self.categoryId = cid;
NSMutableArray * m = [[NSMutableArray alloc] init];
[self setArticles:m];
[m release];
m = [[NSMutableArray alloc] init];
[self setSubcategories:m];
[m release];
m = [[NSMutableArray alloc] init];
[self setSeeAlso:m];
[m release];
self.articleCount = 0;
return self;
}

Assuming your property is declared like below, I don't see any problem with the line you highlighted, nor with the rest of your code:
#property (retain) Category *c;
Just shooting in the dark, but last time Xcode, Instruments and the Simulator did crazy stuff, I had an infinite recursion going on.
Are you sure your method fillCategory doesn't somehow call your viewDidLoad or reset method?
What happens when you set a breakpoint on the first line of your reset method, and follow your code step-by-step?

Related

Why does my UINavigationController crash on release configuration?

I have a requirement to remove a previous view controller in a stack. I have successfully used this method in the past, without any problems:
NSMutableArray *vcs = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
BOOL removedFlag = YES;
while (removedFlag == YES)
{
removedFlag = NO;
for (UIViewController *vc in vcs)
{
if( ![vc isKindOfClass:[self class]] && ![vc isKindOfClass:[MenuVC class]] )
{
[vcs removeObject:vc];
removedFlag = YES;
break;
}
}
}
[self.navigationController setViewControllers:[NSArray arrayWithArray:vcs]];
Now I’m updating the app, and it mysteriously crashes on release configuration, not on debug. In addition, there are no warnings whatsoever in debug mode. I think something changed with the SDK. It never did this before.
I’ve made a sample project available:
http://dl.dropbox.com/u/7834263/RemoveTest.zip
This is an ARC-enabled project.
The first line should actually be:
NSMutableArray *vcs = [self.navigationController.viewControllers mutableCopy];
NSMutableArray is a subclass of NSArray. arrayWithArray is a method of the NSArray class -- it creates an immutable array. The mutableCopy method creates a mutable copy of the original array. You never really should have been able to call removeObject on your vcs array because it was never actually an NSMutableArray -- it was an NSArray.
A simple way to remove the previous viewController in the stack could be:
int vcIdx=[self.navigationController.viewControllers indexOfObject:self]-1;
NSMutableArray *vControllers = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
[vControllers removeObjectAtIndex:vcIdx];
self.navigationController.viewControllers=vControllers;

Memory Leak with Collection

Please take a look at the following code.
XmlManipulatorObject=[[xmlManipulator alloc] init];
self.QuestionMutableArray=[[XmlManipulatorObject ReadXml] init];
self.dictionary=[[NSMutableDictionary alloc]init];
self.dictionary=[QuestionMutableArray objectAtIndex:reloader];
in my dealloc method i need to release all the objects i am using above.What should be the way?
I tried with the following but having BAD_ACCESS :
-(void)deallocAll
{
[self.dictionary release];
[self.XmlManipulatorObject release];
[self.QuestionMutableArray release];
}
and the properties are as below :
#property(nonatomic,retain)NSMutableDictionary *dictionary;
#property(nonatomic,retain)NSMutableArray *QuestionMutableArray;
#property(nonatomic,retain)xmlManipulator *XmlManipulatorObject;
Ok you have a number of things wrong/ bad ideas happening here:
Variable and method names should start with a lowercase letter. This helps with readability and avoids confusion with class names. So XmlManipulatorObject should be xmlManipulatorObject, ReadXml should be readXml, etc.
When you create an instance of a class, you either should autorelease it or manually release it. For example:
self.dictionary=[[[NSMutableDictionary alloc] init] autorelease];
or
NSMutableDictionary *myDic = [[NSMutableDictionary alloc]init];
self.dictionary=myDic;
[myDic release];
From the above it also appears that you don't know about the way that properties handle retains and releases. Do some reading about properties.
Sometimes it is far better to release your objects immediately after your done using them rather than at the dealloc method.
Also you should probably use the default dealloc method.
Also since you never called the dealloc super method you wont be releasing any memory.
-(void)deallocAll
{
[self dealloc];
[self.dictionary release];
[self.XmlManipulatorObject release];
[self.QuestionMutableArray release];
}
I would even go as far to change this from deallocAll to just dealloc.
drekka is right. You should note the below point too,
If you are using synthesized properties, set your variable to nil instead of releasing it.
self.yourObject = nil;
This will send the setter method 'nil' as a parameter, and your object will be released by the setter method.
i dont know what is the purpose of deallocAll. Well the best way i learned so far for that case:
NSObject *ob = [[NSObject alloc] init];
self.myObject = ob;
[ob release];
And in dealloc
-(void)deallocAll{
[super dealloc];
[myObject release];
}

Need Help with applicationDidBecomeActive

I have been trying for days to get this code to work, but I have no idea what I am doing wrong. Everytime the app wakes up from sleep, or the user closes the app and opens it again (without closing the app from multitasking), I want a label value to change.
In my applicationDidBecomeActive, I am running a counter, which I want to display on whatever viewcontroller is open at that moment.
Code:
- (void)applicationDidBecomeActive:(UIApplication *)application {
counter = counter + 1;
W1G1 *view1 = [[[W1G1 alloc] initWithNibName:#"W1G1" bundle:nil] retain];
[view1 setlabel];
}
In my viewcontroller W1G1, I have the following code:
Code:
- (void) setlabel {
NSString *string = [NSString stringWithFormat:#"%d", counter];
vocabword.text = string;
}
I have imported W1G1 in my appdelegate, but the code does not run :( Please help!
Thanks
In the AppDelegate.m file, where you have
- (void)applicationDidBecomeActive:(UIApplication *)application {
counter = counter + 1;
W1G1 *view1 = [[[W1G1 alloc] initWithNibName:#"W1G1" bundle:nil] retain];
[view1 setlabel];
}
the variable counter being incremented is confined to the AppDelegate. In other words, your view controller doesn't know that it has been incremented.
I would suggest that you use NSUserDefaults to store the value of counter so that you can easily pass it between these view controllers. Either that, or you could allow for an input into the method setLabel, e.g.
- (void) setlabel:(int)counter {
NSString *string = [NSString stringWithFormat:#"%d", counter];
vocabword.text = string;
}
and then in the AppDelegate you'll want to do:
- (void)applicationDidBecomeActive:(UIApplication *)application {
counter = counter + 1;
W1G1 *view1 = [[[W1G1 alloc] initWithNibName:#"W1G1" bundle:nil] retain];
[view1 setlabel:counter]; // <-- now you're using counter
[self.window addSubview:view1];
}
1) When you say 'the code does not run' do you mean that? That is, if you put NSLogs in applicationDidBecomeActive: and in setLabel does it show the code is run?
2) I would suspect the code is running. But your code won't "show the counter on whatever view controller is open at that moment". Your code creates a new view (view1), but that view won't be displayed. It is not added as a subview to anything. Your code will also leak. You create a W1G1 object, but it is never released and you throw away any reference you have to it.
To achieve what you want, you could add a subview to the application's window. Depending how your app delegate is set up, something like the following should do the trick:
counter++;
W1G1 *viewController1 = [[W1G1 alloc] initWithNibName:#"W1G1" bundle:nil];
[viewController1 setlabel: counter];
[[self window] addSubview: [viewController1 view]]
// you'll want to save a reference to the viewController somehow so you can release it at a later date
Then in W1G1
- (void) setlabel: (int) counter;
{
NSString *string = [NSString stringWithFormat:#"%d", counter];
vocabword.text = string;
}
There are, of course, lots of other approaches you could take towards this problem. And you'll need some strategy for removing the W1G1 view that you are adding at some stage, otherwise you'll just get more and more views added.
Update: You ask (in comments) how to keep track of your viewController throughout lifetime of the app... One approach is to keep track of it in your appDelegate. In the header have something like:
#class W1G1;
#interface MyAppDelegate : : NSObject <UIApplicationDelegate>
{
// other decelerations
int counter;
W1G1 * _myW1G1
}
#property (nonatomic, retain) W1G1* theW1G1
In the .m file include
#synthesize theW1G1 = _myW1G1;
Probably in application:didFinishLaunchingWithOptions: create the viewController, set the property to refer to it, and add its view to the view hierarchy.
W1G1* theViewController = [[W1G! alloc] initWithNibName: #"W1G1" bundle: nil];
[[self window] addSubview: [theViewController view]];
[self setTheW1G1: theViewController];
[theViewController release];
Then when you want to access the viewController again from with the app delegate use [self theW1G1], e.g.
[[self W1G1] setlabel: counter];

EXC_BAD_ACCESS and Zombies, Yet not really sure why it keeps coming up

I don't know what's going wrong here. The crash happens when switching back and forth between views.
Here's what instruments gives me:
Clicking into it references this code with the first action :
-(IBAction)pushnews; {
NewsViewController *news = [[[NewsViewController alloc]init]autorelease];
news.title =#"Page";
[self.navigationController pushViewController:news animated:YES]; }
I use autorelease sometimes but usually I just release it my self. Should I get rid of autorelease and add [news retain]
What am I doing wrong?
Edit based on answers:
Following EmptyStack's Advice: ViewWillDisappear Code looks like this:
- (void)viewWillDisappear:(BOOL)animated {
webView.delegate = nil; }
This seems to resolve issues (pending more testing)
In viewdidload I said: webView.delegate = self;, which may have been the issue!
My guess is that, there is a UIWebView in NewsViewController, and it is causing the crash. It is possible that, a delegate method of web view is called after the web view is released. If so, try to setwebView.delegate = nil; in NewsViewController's viewWillDisappear: method.
try this instead :
-(IBAction)viewcontroller;
{
NewsViewController *news = [[NewsViewController alloc]init];
news.title =#"Page";
[self.navigationController pushViewController:news animated:YES];
[news release];
}

encountering numerous leaks on iphone device when using NSOperationQueue and trying to change sliders / pickers etc

encountering numerous leaks on iphone device when using NSOperationQueue and trying to change sliders / pickers etc.
I am able to change labels without an issue, but if I try to change a slider or picker both created on interface builder I get these leaks.
Leaked Object # Address Size Responsible Library Responsible Frame
GeneralBlock-16 0x1b00a0 16 GraphicsServices GetFontNames
GeneralBlock-16 0x1aea90 16 WebCore WebThreadCurrentContext
GeneralBlock-16 0x1aea80 16 GraphicsServices GSFontGetFamilyName
GeneralBlock-64 0x1a7370 64 UIKit GetContextStack
code below
- (void)loadData {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(firstRun)
object:nil];
[queue_ addOperation:operation];
[operation release];
}
- (void)firstRun {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
[self setSliders];
NSLog(#"firstRun method end");
[pool drain];
}
- (void)setSliders {
NSMutableArray *tempArray = [[[NSMutableArray alloc]init] autorelease];
aquaplannerAppDelegate *appDelegate = (aquaplannerAppDelegate *)[[UIApplication sharedApplication] delegate];
tempArray = appDelegate.settingsValuesArray;
freshMarineSegment.selectedSegmentIndex = [[tempArray objectAtIndex:0]intValue];
for (int i = 1; i <= 20; i++ ) {
UILabel *label = (UILabel *)[self.view viewWithTag:200+i]; // gets label based on tag
UISlider *slider = (UISlider *)[self.view viewWithTag:100+i]; // gets slider based on tag
slider.value = [[tempArray objectAtIndex:i]intValue];
label.text = [[[NSString alloc] initWithFormat:#"%#",[tempArray objectAtIndex:i]] autorelease];
[label release];
[slider release];
}
}
I'm assuming you're doing something else before setSliders for which you created the NSOperation, and you just omitted that code.
UIKit is not guaranteed to be thread safe, and you should only access your interface elements on the main thread. This is mentioned in a few places in the docs, but the most telling is in the Cocoa Fundamentals Guide:
All UIKit objects should be used on the main thread only.
So firstRun should look more like this:
- (void)firstRun {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// Do something important here...
[self performSelectorOnMainThread:#selector(setSliders) withObject:nil waitUntilDone:NO];
NSLog(#"firstRun method end");
[pool drain];
}
Why are you using an NSMutableArray in setSliders? You're never actually changing the array, and mutable data structures can wreak havoc in threaded programming.
Also, I'd rename the setSliders method to something like updateSliders. It's a Cocoa style issue. Methods that start with "set" should be used for mutating a single instance variable/property.
You are releasing each label and slider in your for loop, even though you have not retained them. This is not correct. You only need to release memory that you have allocated, copied or retained. See the memory management programming guide for more details.
I would also change the way you initialize your temporary array. The designated initializer for NSMutableArray is -initWithCapacity:, not -init. There is also a class method designed to return an autoreleased instance for convenience:
NSMutableArray *tempArray = [NSMutableArray arrayWithCapcity:0];