I have been puzzling over this for days now. I'm still trying to wrap my head around memory management in objective-c.
Here is my snippet (condensed for clarity):
- (void)performOperation:(NSString *)operation
{
if ([#"+" isEqual:operation])
{
waitingOperation = operation;
}
else if ([#"C" isEqual:operation])
{
waitingOperation = nil;
}
}
waitingOperation is merely a local private NSString (no #property, no #synthesize, no getters/setters).
Shouldn't I be leaking memory when I assign waitingOperation to nil when it's currently pointing to an NSString on the heap? My call to this method is in an ios app that is passing NSString from UILabel display.text. I've been profiling this code with Instruments and I never see any leaks. I would really appreciate some clarity on this. Thanks in advance.
You haven't laid a claim of ownership on waitingOperation by calling retain, so you have no responsibility to release.
This may lay you open to problems at some point if the string is released elsewhere (by disposing of the UILabel for example), in which case you'll be left with a dangling pointer. But you aren't leaking anything here.
Related
I encounter memory leak with stringWithCString, can anybody find the memory leak in stringWithCstring function?
SomeView *detailViewController = [[SomeView alloc] initWithNibName:#"SomeView" bundle:nil];
detailViewController.test = [NSString stringWithCString:"abc" encoding:UTF8_ENCODING];
the property in SomeView for test variable is
#property (nonatomic,copy) NSString* test;
Is my property declared correctly?
Are you releasing the string in your [SomeView dealloc] method like this:
- (void)dealloc
{
self.test = nil;
[super dealloc];
}
Whatever you use to "detect" the leak, how reliable is that?
Now I've never used properties, but the code above looks pretty correct -- the string should get released. The "stringWithCString:" will autorelease the string anyway, so there's no problem. However, the "copy" in your property seems to indicate that it makes a copy (huh? Surprise!) of the string, and even if I assume that object-type properties are released when the owning object dies -- if the owning object never dies then the copied string will never be released.
Maybe the memory leak detector really meant to say that the copy of that string is never released because you forgot to release the "detailViewController"? That copy would still be in the same source code line, so even if the memory leak detector can give an accurate location but only provides a line number you could be misled...
I ran into this problem while trying to fix a memory leak with the facebook-ios-sdk. How do i handle this situation when passing objects from no arc compiled classe to arc enabled classe?
This is the code inside the non arc compiled Facebook library: (i removed the unnecessary stuff which is not related to the problem) as you can see, result object is not autoreleased or released.
- (void)handleResponseData:(NSData *)data {
NSError* error = nil;
id result = [self parseJsonResponse:data error:&error];
self.error = error;
// Call the defined delegate wich is my AppDelegate didLoad method wich is arc enabled
[_delegate request:self didLoad:result];
}
- (id)parseJsonResponse:(NSData *)data error:(NSError **)error {
SBJSON *jsonParser = [[SBJSON alloc] init];
//gets the object wich leaks or gets overreleased
id result = [jsonParser objectWithString:responseString];
[jsonParser release];
return result;
}
Now if i try to add autorelease to the result object, i am facing a NSZombie when my arc code in my AppDelegate try's to release the object. However if i leave the code like this i'm facing memory leaks whit the result object which gets not released.
am i missing something basic? i can't get my head around this?
Thanx for any advice! Chris
The result returned from -parseJsonResponse:... is autoreleased already (see note at bottom).
Since the name of the -parseJson method doesn't begin with new, alloc, copy or mutableCopy, the compiler assumes that it returns an object with a +0 reference count, meaning it needs to be retained by the calling code if it is to be kept around, and doesn't need to be released if it's not being kept around. That's a long winded way of saying that it should neither leak nor cause a crash in your ARC code as written in your question.
Passing objects between ARC code and manual reference counting code doesn't require any special handling. You just need to make sure that methods' names match their memory management semantics in the non-ARC code. It certainly seems like you've done that in this case, although as you say, you didn't post your complete code.
Note: Presumably, objectWithString: returns an autoreleased object. If it doesn't it, it should (because it doesn't start with alloc, new, copy, mutableCopy).
This code down below works as expected, cleans things up without Zombies. The class in which this method exists, is the owner of the Nodes, which are being released, yet, upon "Analyze" the following 2 issues show up.
If possible, could you help me understand why?
- (void) dealloc {
NSLog(#"Releasing [Doubly Linked List] .. ");
Node *thisNode = [self firstNode];
Node *nextNode = [thisNode next];
while (nextNode != nil) {
// If "Next node" is not nil, it means that
// "previous node" can now be released
NSLog(#" - releasing node \"%c\"", [[nextNode previous] charData]);
[[nextNode previous] release];
nextNode = [nextNode next];
}
[[self lastNode] release];
[super dealloc];
}
Click on the icon on the left of the message, it will show the path through the code that produces the error.
You are releasing something, [nextNode previous] that you do not own. In particular you did not alloc or retain it nor obtain it from a method that begins with new or copy so you do not have ownership of it and should not release it.
It is also very uncommon to release something not in your class, [[nextNode previous] release].
Now: [[self lastNode] release];
As above you did not obtain an ownership on the object you are releasing.
If lastNode is a property with retain you are subverting the setter and when later a value is assigned to lastNode via the setter there will be an extra release on the object and probably a crash. If it is not a property again this it very non-standard to release something that is returned by a method call.
Any releases with code of this form [[self lastNode] release] is non-standard and if be avoided there will be fewer ownership (retain/release) problems.
You will save a lot of time and grief by studying the Apple memory management documentation.
The issue is that you are not doing things according to the ordinary memory management conventions of Objective C and the static analyzer is getting confused. Basically, the Objective C class which allocates an object "owns" the object and is responsible for releasing it. Here, you are not using those conventions, so the analyzer is complaining, even if what you are doing works correctly.
See Apple's documentation for more on ownership.
Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.
I'm fairly new to Cocoa and Objective-C. Currently I'm developing a fairly basic application to test my knowledge and put some of the stuff I've been reading about into practice. Everything is working, but Leaks reports a number of issues.
None of these leaks seems to be directly applicable to code that I've written (I have read and tried to follow Apple's rules on memory allocation). Currently my project makes use of Garbage Collection and I'm developing on Snow Leopard. Running AnalysisTool finds no issues with my code (aside from a few naming convention warnings).
Currently my application makes use of an NSTableView which I have hooked up to an NSArrayController. Interacting with the NSTableView seems to cause leaks to report issues (actions such as sorting table columns and other standard user interaction). This leads me to believe that my use of the NSArrayController (and my implementation of its content source) is to blame.
Currently the NSArrayController receives its content from an NSMutableArray (timers) handled in my Application's delegate like so:
- (id) init
{
if (self = [super init])
{
timers = [NSMutableArray array];
}
return self;
}
- (void) dealloc
{
[timers release];
[super dealloc];
}
Within Interface Builder my NSArrayController has its Object Controller set to the Timing class, which is defined below:
#interface Timing : NSObject {
NSString *desc;
NSDate *timestamp;
bool active;
}
#end
#implementation Timing
-(id) init
{
if (self = [super init])
{
desc = #"New timing";
timestamp = [[NSDate alloc] init];
active = false;
}
return self;
}
-(void) dealloc
{
[timestamp release];
[super dealloc];
}
#end
I've used standard Cocoa bindings to hook up Add and Remove buttons to manipulate the TableView and these seem to work correctly (clicking Add will create a row in the TableView with the value of 'New timing', for instance).
Leaks reports that the libraries responsible are AppKit and CoreGraphics. Although, honestly, I'm still new to the Leaks tool - so I could be reading its output incorrectly. If it helps, I've placed a screenshot of its output here. If anyone could point me in the right direction, that would really be appreciated.
As an aside, I've also been experimenting with manually adding objects to the timers array without the use of Cocoa bindings. Here's what I came up with:
Timing *timingInstance = [[Timing alloc] init];
[timers addObject:timingInstance];
[timingInstance release];
[timersController setContent:timers];
[timersTableView reloadData];
Again, this seems to work, but I thought it best to ask the experts!
Your memory management for the timers array is not quite correct. Using the array factory method will return an instance of NSMutableArray that has already been autoreleased, so the lifetime of that object is (probably) limited to the end of the current run loop, and it will be over-released when you call release in your dealloc method. The proper way to do it is as follows:
- (id) init
{
if (self = [super init])
{
timers = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
This method will give you an instance of NSMutableArray with a retain count of 1, which will then drop to zero (and properly release the memory) when you call release in your dealloc method. The call to alloc in your init method is balanced out by the call to release in your dealloc method. I notice that this is the exact pattern that you used for your NSDate object in the Timing class, so you are already familiar with the idea.
Your code as written is not leaking. The Cocoa frameworks will sometimes generate false leak reports when run under leaks, as certain things such as singletons and caches which are used in the implementation of the frameworks will sometimes show up as leaks even though they're not.
You're probably better off running the ObjectAlloc and/or ObjectGraph instruments to get an idea of when your objects are being allocated and deallocated.
Currently my project makes use of
Garbage Collection and I'm developing
on Snow Leopard
I don't understand. You're using garbage collection right? If so then GC takes care of releasing objects for you so anywhere you use "release" does absolutely nothing. Release calls are ignored when GC is enabled. You only use release when you're managing the memory yourself which is when GC is off. Also, your dealloc methods do nothing too. In GC that method is never used. When you create an object and then you finish with it you tell GC that it's OK to get rid of the object by setting it to nil. That's all you have to do. There is no "release" needed or dealloc method needed. Just set things to nil or or not as needed.