I create an object in each iteration of a for loop. However the dealloc function is never called. Is it not supposed to be released at each iteration? I am using ARC and I have NSZombies deactivated. I don not see either any circular reference. Running the memory leak instruments from xcode it does not show any leaks, however the pointers memory of the class are never freed and the dealloc call never done. Any idea why this could happen?
Thank you!
for(int i=0; i<10; i++)
{
//calculate the hog features of the image
HogFeature *hogFeature = [self.image obtainHogFeatures];
if(i==0) self.imageFeatures = (double *) malloc(hogFeature.totalNumberOfFeatures*sizeof(double));
//copy the features
for(int j=0; j<hogFeature.totalNumberOfFeatures; j++)
self.imageFeatures[i*hogFeature.totalNumberOfFeatures + j] = hogFeature.features[j];
}
The HogFeature class declaration looks like this:
#interface HogFeature : NSObject
#property int totalNumberOfFeatures;
#property double *features; //pointer to the features
#property int *dimensionOfHogFeatures; //pointer with the dimensions of the features
#end
and the implementation:
#implementation HogFeature
#synthesize totalNumberOfFeatures = _totalNumberOfFeatures;
#synthesize features = _features;
#synthesize dimensionOfHogFeatures = _dimensionOfHogFeatures;
- (void) dealloc
{
free(self.features);
free(self.dimensionOfHogFeatures);
NSLog(#"HOG Deallocation!");
}
#end
Finally, the call to obtainHogFeatures inside the UIImage category looks like:
- (HogFeature *) obtainHogFeatures
{
HogFeature *hog = [[HogFeature alloc] init];
[...]
return hog;
}
You might want to enclose the inner loop with an #autoreleasepool { ... } which tells the compiler when to do the disposal, otherwise the pool will only be emptied when control returns to the main loop.
for (i = 0; i < 10; i++) {
#autoreleasepool {
...
}
}
As pointed out by CodeFi in the comments:
This will create a new autoreleasepool for each iteration of the loop, which would destroy each object after the iteration is completed, but would make the program do more work. If you don't mind all the objects hanging around until after the loop is completed, you would put the #autoreleasepool outside of the outer loop
#autoreleasepool {
for (i = 0; i < 10; i++) {
...
}
}
The reason the objects are sticking around is because they aren't being released. I don't see the declaration of self.imageFeatures - is this an array? If the features are being put in to an array, they won't be released as long as they remain in the array or the array itself isn't released.
I'm a little confused by the use of the C malloc and (attempted) free calls. There may very well be a motivation here I'm not aware of, but, given what you have provided, here is how I would write this, and I'd be surprised if the deallocs aren't triggered as expected:
NSMutableArray *features = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; i++)
{
NSArray *hogFeatureArray = [[self image] obtainHogFeatures];
for (HogFeature *feature in hogFeatureArray)
{
[features addObject:hogFeature];
}
}
[self setImageFeatures:features];
The imageFeatures property is:
#property (nonatomic, retain) NSMutableArray *imageFeatures;
Assuming you've put all your hog feature instances into this imageFeatures array, they will be retained by that imageFeatures array. In order to observe your dealloc in action, one of two things needs to happen: You either need to remove a hog feature from the array, or you need to release the array itself (this would be done by setting the pointer to nil):
[self setImageFeatures:nil] // Previously assigned array now released
[[self imageFeatures] removeAllObjects]; // Works alternatively
Related
There was a case when not explicitly initilizing a local CGFloat to 0 would result the variable holding garbage:
-(void)foo
{
CGFloat aFloat;
NSLog(#"float:%f", aFloat);
aFloat = 70;
}
[self foo];
[self foo];
Output:
float:0
float:70
So it really should print 0 both times, but since I didn't explicitly initialize the float to 0, it contained garbage the second time around. My question is, does this apply to objects as well? Is there a difference for local variables between these two options:
1. NSObject *object;
2. NSObject *object = nil;
A pointer is initially nil if it is an ivar. (It is an ivar if you have declared it in the #interface part of your class.) If the pointer is a local variable (you declared it in a method) it will contain garbage. It is best practice to always assign something right away.
Update: As pointed out in the comments by omz, if you are using ARC, your pointers are also nilled if they are local variables.
it contained garbage the second time around
Actually it didn't contain garbage. It contained the same value that was written to that place in memory previously. It just happened that stack didn't got chance to get overwritten by any new value, so when you called foo the second time the aFloat variable got mapped to the same location.
For more specific description I highly recommend one of the best answers of all time.
Yes the same applies; the object pointer is a variable just like the float:
test.m:
#include <Foundation/Foundation.h>
#interface Foo : NSObject
{
NSString *ivarString;
}
- (void)foo;
- (void)test;
#end
#implementation Foo
- (void)foo
{
NSString *stackString;
NSLog(#"stackString='%#', ivarString='%#'", stackString, ivarString);
stackString = #"Hello";
ivarString = #"World";
}
- (void)test
{
[self foo];
[self foo];
}
#end
int main(int argc, const char **argv)
{
#autoreleasepool
{
Foo *foo = [[[Foo alloc] init] autorelease];
[foo test];
}
return 0;
}
Output:
2012-08-06 06:52:36.123 test[15293:403] stackString='(null)', ivarString='(null)'
2012-08-06 06:52:36.126 test[15293:403] stackString='Hello', ivarString='World'
Note that this test project uses MRR, not ARC.
I want to store various objects in an NSCache and have them automatically get removed when a memory warning hits. So I wrote the following NSDiscardableContent implementation that I use to wrap instances I put into the values in an NSCache.
But I'm never seeing them get removed from the cache when I run "Simulate Memory Warning". Is there something wrong with my NSDiscardableContent implementation? Or do I need to do something else to make the cache drop the values when a memory warning occurs?
/** #brief generic implementation of the NSDiscardableContent for storing objects in an NSCache */
#interface GenericDiscardableObject : NSObject<NSDiscardableContent>
#property (nonatomic, retain) id object;
#property (nonatomic) NSUInteger accessCount;
+ (GenericDiscardableObject *)discardableObject:(id)ob;
#end
#implementation GenericDiscardableObject
#synthesize object, accessCount;
+ (GenericDiscardableObject *)discardableObject:(id)ob {
GenericDiscardableObject *discardable = [[GenericDiscardableObject alloc] init];
discardable.object = ob;
discardable.accessCount = 0u;
return [discardable autorelease];
}
- (void)dealloc {
self.object = nil;
[super dealloc];
}
- (BOOL)beginContentAccess {
if (!self.object)
return NO;
self.accessCount = self.accessCount + 1;
return YES;
}
- (void)endContentAccess {
if (self.accessCount)
self.accessCount = self.accessCount - 1;
}
- (void)discardContentIfPossible {
if (!self.accessCount)
self.object = nil;
}
- (BOOL)isContentDiscarded {
return self.object == nil;
}
#end
As far as I can tell, the default behaviour of NSCache is to throw objects away when there is a memory warning.
So you can simply store your objects "naked" in your cache as if it was an NSDictionary, and they will be cleaned up when memory gets tight. You don't have to wrap them in a discardable object, or do anything else. E.g.
[myCache setObject:foo forKey:#"bar"]; // foo will be released if memory runs low
It's not very clear from the documentation, but as far as I can tell, the purpose of the <NSDiscardableContent> content protocol is for implementing more complex behaviour whereby an object can release subcomponents when memory is low, without necessarily releasing itself.
I am using a Nib as a template for several buttons. It seemed to work fine, they each have their own independent state. However when I went to release the buttons I would crash in the dealloc. Here is the code...
mSoundBtns = new cSoundButton*[mNumSounds];
for(unsigned int i = 0 ; i < mNumSounds; ++i) {
mSoundBtns[i] = nil;
}
for(unsigned int s = 0; s < mNumSounds; ++s) {
[[NSBundle mainBundle] loadNibNamed:#"InstanceSoundButton" owner:self options:nil];
//Auto Loads via Outlet into 'soundNib'
mSoundBtns[s] = soundNib;
soundNib = nil;
uint32 count = mSoundBtns[s].retainCount;
NSLog(#"Last Count: %d", count);
}
for(unsigned int j = 0; j < mNumSounds; ++j) {
[mSoundBtns[j] release]; //**** Crash here on 7th (of 8) release
mSoundBtns[j] = nil;
}
Header:
#interface cLocationContext {
...
cSoundButton** mSoundBtns;
IBOutlet cSoundButton* soundNib;
}
#property (nonatomic, assign) IBOutlet cSoundButton* soundNib;
#end
The Nib is very simple, it just include a parent view and a child view of a custom view type.
cSoundButton simply keeps track of a name and a boolean state Mute or Not. Here is the dealloc
- (void)dealloc {
delete[] mSoundTag;
// Call the inherited implementation
[super dealloc]; //****Crashes in here
}
The crash is inside the call to super dealloc, in UIButton -> UIButtonContent dealloc. I assume I am doing something poor with my memory management like deallocing twice but I can't spot where.
Is what I am doing by loading the nib multiple times legal?
You have to retain the button as soon as you load it from the NIB. If you don't, you are not allowed to release it later, and you won't be able to access the button once your code returns control to the runloop (when the autorelease pool is drained).
PS: Wouldn't it be easier to use a Cocoa collection (NSMutableArray) to store the references to the buttons? Your code looks too complicated to me.
It will greatly simplify your memory management if you use your property and use an NSArray to store the button instances.
[[NSBundle mainBundle] loadNibNamed:#"InstanceSoundButton" owner:self options:nil];
//Auto Loads via Outlet into 'soundNib'
[mSoundBtns addObject:self.soundNib];
self.soundNib = nil;
Later, when it's time to release
[mSoundBtns release];
Keep in mind that when you're using properties you've got to reference them through self. The following two lines are exactly equivalent:
self.soundNib = something;
[self setSoundNib:something];
When you set soundNib = nil you are setting the variable soundNib to nothing, losing the reference to the button you loaded. If you hadn't added the pointer to an array and released it later you'd be leaking everything. Technically the way you're doing it might work... but don't do it that way. Using proper NSArrays and properties will make this whole process significantly easier and more maintainable.
Is it possible to have a variable outlet name?.
For example you have 10 labels (perhaps seats on bus). each has an outlet, seat1 seat2 etc.
Is it possible to have a for loop
that concatenates #"seat" to the increment integer. So that I can access seat1, seat2 outlet without having to specify it individually.
This doesn’t work but makes it a bit clearer what I am trying to achieve.
int i;
for (i = 0; i < [seatarray count]; i++)
{
[#”seat” stringByAppendingString[ i stringValue]] = #””;
}
Starting iOS4 you can use IBOutletCollection which allows to connect multiple instances to a single outlet which represents an array of objects, e.g. IBOutletCollection which can store UILabels only:
#property (nonatomic, retain) IBOutletCollection(UILabel) NSArray *seats;
It might be easier to simply create the array yourself at load time:
self.seatarray = [NSArray arrayWithObjects:seat1, seat2, ..., seatN, nil];
You should be able to do that with key-value coding, something like (untested code)
for (int i = 0; i != 10; ++i) { [self setValue:#"foo" forKey:[#"seat" stringByAppendingFormat:#"%d", i]]; }
I created an "SDMutableGrid" class so that I could use a grid. It's just a child of NSMutableArray that contains a number for arrays equal to the number of rows in the grid.
Currently, the program quits before it really starts and it appears that it is because the methods defined for NSMutableArray somehow do not apply to SDMutableGrid, anyone know why?
Here is the .h :
#import <Foundation/Foundation.h>
#import "SDDimensions.h"
#interface SDMutableGrid : NSMutableArray {
SDDimensions dimensions;
}
#property (nonatomic) SDDimensions dimensions;
- (id)initWithDimensions:(SDDimensions)newDimensions;
- (void)addObject:(id)anObject toRow:(NSUInteger)row;
#end
Here is the .m :
#import "SDMutableGrid.h"
#implementation SDMutableGrid
#synthesize dimensions;
- (void)setDimensions:(SDDimensions)newDimensions {
if (newDimensions.width < dimensions.width) {
NSMutableArray *anArray;
NSRange aRange = NSMakeRange(newDimensions.width, dimensions.width - newDimensions.width);
for (NSUInteger i = 0; i < MIN(dimensions.height,newDimensions.height); i++) {
anArray = [self objectAtIndex:i];
[anArray removeObjectsInRange:aRange];
}
}
dimensions.width = newDimensions.width;
if (newDimensions.height > dimensions.height) {
for (NSUInteger i = dimensions.height; i < newDimensions.height; i++) {
[self addObject:[[NSMutableArray alloc] initWithCapacity:dimensions.width]];
}
} else if (newDimensions.height < dimensions.height) {
[self removeObjectsInRange:NSMakeRange(newDimensions.height, dimensions.height - newDimensions.height)];
}
dimensions.height = newDimensions.height;
}
- (id)initWithDimensions:(SDDimensions)newDimensions {
if (self = [super initWithCapacity:newDimensions.height]) {
NSMutableArray *anArray;
for (NSUInteger i = 0; i < newDimensions.height; i++) {
anArray = [[NSMutableArray alloc] initWithCapacity:newDimensions.width];
NSLog(#"Got this far");
[self addObject:anArray];
NSLog(#"woot");
[anArray release];
}
NSLog(#"Finished Initializing grid");
}
return self;
}
- (void)addObject:(id)anObject toRow:(NSUInteger)row {
[[self objectAtIndex:row] addObject:anObject];
}
#end
And here is what is appearing on the console:
2009-08-12 15:27:02.076 Flipswitch[1756:20b] Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: ' -[NSMutableArray initWithCapacity:]: method only defined for abstract class. Define -[SDMutableGrid initWithCapacity:]!'
2009-08-12 15:27:02.080 Flipswitch[1756:20b] Stack: (
807902715,
2536648251,
808283725,
808264737,
13690,
11018,
10185,
814713539,
814750709,
814739251,
814722434,
814748641,
839148405,
807687520,
807683624,
814715661,
814752238,
10052,
9906
)
The short, easy answer: Don't make a subclass of NSArray. It's better to make a category on NSArray or make an NSObject subclass that has an NSArray ivar that you talk to.
The long, technical answer: NSArray is a class cluster. This means that it isn't actually one class, but many classes operating under the NSArray abstract class interface that are each implemented in a different way (say, one implementation for small arrays, another for big arrays, etc.). To create a subclass of a class cluster, you have to implement all the primitive methods of the abstract class you are inheriting from, manage your own storage and basically reimplement all the stuff you were hoping to get for free by subclassing.
More simply, you could just create a category if you don't require additional ivars. If you want an object that behaves like an array with additional state, you can create a class that has an NSArray and use Objective-C message forwarding to forward everything except your custom behavior to that class.
This is due to the nature of 'Class Clusters' used for collection classes in Foundation.
See:
Class Clusters
Basically, NSMutableArray defines a public interface to 'mutable arrays', but is not the actual class you use when initialized. So 'initWithCapacity:' is defined, but not implemented in NSMutableArray. If you run:
NSMutableArary *foo = [[NSMutableArray alloc] init];
NSLog(#"%#", [foo className]);
you will print "_NSCFArray", which is a concrete subclass of NSMutableArray (and NSArray). To work around this, I would have a instance variable that is an NSMutableArray, or implement 'initWithCapacity:' with a suitable meaning (such as a capaciy of '3' means a 3x3 grid).
Ok, I found the answer from this question
Although the questions are different, the answer is the same and that is that due to the setup of NSArray (and therefore NSMutableArray), you cannot subclass it without implementing the methods yourself.
So I guess I'll just make SDMutableGrid have an NSMutableArray variable instead of actually being an NSMutableArray.
You problem is that you are not implementing abstract methods of NSMutableArray super class that need to be implemented, it says
-[NSMutableArray initWithCapacity:]: method only defined for abstract class. Define -[SDMutableGrid initWithCapacity:]!' 2009-08-12 15:27:02.080 Flipswitch[1756:20b]
So you need do define initWithCapacity in your subclass, I would recommend to not extend NSMutableArray, there is no need, just make a class that has a mutable array in it.