How do I release C style arrays? - objective-c

I have a QuantumClone class which has an array of CGPoints. The single QuantumPilot object creates a QuantumClone at the beginning of each level. During the next level the QuantumPilot records its velocities to its QuantumClone. At the beginning of a new level the game loop runs this code
QuantumClone *c = [[self.pilot clone] copy];
c.bulletDelegate = self;
c.weapon = self.pilot.weapon;
[self.clones addObject:c];
But eventually the game will be reset and each QuantumClone object in the clones NSMutableArray will be removed.
Am I leaking memory by assigning values to the CGPoint pastVelocities[4551] ?
How do I reset these? I can't release them since they are not Objective-C objects. Do I need to call C functions to release this memory?
#interface QuantumClone : QuantumPilot <NSCopying> {
CGPoint pastVelocities[4551];
}
- (id)copyWithZone:(NSZone *)zone {
QuantumClone *c = [[[QuantumClone alloc] init] autorelease];
c.weapon = self.weapon;
for (NSInteger i = 0; i < 4551; i++) {
[c recordVelocity:pastVelocities[i] firing:pastFireTimings[i]];
}
[c recordLatestIndex:timeIndex];
return c;
}
- (void)recordVelocity:(CGPoint)vel firing:(BOOL)firing {
CGPoint p = pastVelocities[timeIndex];
p.x = vel.x;
p.y = vel.y;
pastVelocities[timeIndex] = p;
bool fired = firing;
pastFireTimings[timeIndex] = fired;
timeIndex++;
}
#interface QuantumPilot : CCNode {}
....
#property (nonatomic, retain) QuantumClone *clone;
- (void)copyDeltas {
[self.clone recordVelocity:ccp(self.vel.x, -self.vel.y) firing:self.firing];
}
- (void)createClone {
self.clone = [[[QuantumClone alloc] init] autorelease];
self.clone.active = YES;
self.clone.weapon = self.weapon;
}

Am I leaking memory by assigning values to the CGPoint pastVelocities[4551] ?
Short answer: No.
Long answer: The array in your code is a big chunk of contiguous memory where all CGRects live in, and it has automatic storage, which means it will be allocated and deallocated automatically (when it goes out of scope). In other words, when its parent object is destroyed, the array will be gone along with those 4551 objects.
You can verify its size by printing the result of sizeof(pastVelocities). Dividing the result by sizeof(CGRect) will tell you how many objects of this type can be stored in it.
A deallocation must be married to an explicit allocation. You only need to release memory that is allocated dynamically (explicitly), for example, using the alloc function family (malloc, calloc, realloc, etc).
How do I reset these?
memset(pastVelocities, 0, sizeof(pastVelocities));
This will reset the entire array.

jweyrich beat me to it, but I'll go ahead and post this in case it helps ;)
––
You aren't leaking. The runtime allocates enough memory to hold all ivars. In this case, each QuantumClone instance will use ~18k (~36k on 64-bit) more memory than a QuantumPilot, since you have told the runtime that it needs to allocate enough ivar storage for 4551 CGPoints.
If pastVelocities were a CGFloat * rather than a CGFloat[4551], you would need to manually allocate the memory via malloc and then call free in -dealloc. However, by declaring it as a fixed size C-array, the runtime handles it (but at the expense of making every QuantumClone a rather huge object).
That said, this whole approach seems fragile. Why 4551? Why C arrays? There is nothing wrong with using C arrays for performance, but I strongly suspect this is premature optimization.

Related

Connective C with ARC relishing objects in background

Background
I come from a C background and find giving up manual memory management extremely distressing. The old objective c retain and release model was ok if not a little clumsy.
I have written a osX app that reads data from an unspecified data source with an unknown size and makes a visual presentation of the data. In order to stress the app I generated a very large dataset which generates over a hundred million objects in less that 3 minutes (timing is a guess) but when I tare down the view containing the mutable arrays with hundred million objects the app beach balled while deallocating the objects. I solved this problem by passing the array to a background thread which did the release. Anyway I had to redesign The app so it could be sandboxed and I decided to use ARC and now I am back to the same stress test and the beach balling is back. Is there a way I can get a background thread to do the release of the objects created under ARC or do I need to go back a non ARC design and what if I wanted to re write the app in Swift.
Regards Christian Andersen
Is there a way I can get a background thread to do the release of the objects created under ARC
Yes. Just assign nil to the variable on the background thread. But you should ensure that the variable is the only one variable for referring the value.
#import <Foundation/Foundation.h>
#import <pthread.h>
pthread_t g_mainThread;
#interface Test : NSObject
#end
#implementation Test
- (void)dealloc
{
NSLog(#"Test %p was dealloced on %s", self,
g_mainThread == pthread_self() ? "the main queue" : "global queue");
}
#end
// main
int main()
{
g_mainThread = pthread_self();
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; ++i) {
Test *test = [[Test alloc] init];
[array addObject:test];
}
__block NSMutableArray *arrayOnGlobalQueue = array;
/*
* The array object was referenced from *array* and *arrayOnGlobalQueue* variables.
* The reference count of the array object is 2.
*/
array = nil;
/*
* The *array* variable doesn't refer the array object any more.
* The reference count of the array object is 1.
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
/*
* Assign nil to the variable for releasing the array object
*/
arrayOnGlobalQueue = nil;
/*
* The *arrayOnGlobalQueue* variable doesn't refer the array object any more.
* The reference count of the array object is 0.
* Thus the array object was dealloced on the background thread(queue).
*/
});
dispatch_main();
return 0;
}
The result is this.
Test 0x7f96aac033b0 was dealloced on global queue
...
But, in common situation, Autorelease Pool refers objects, and Autorelease Pool releases the objects on the main thread.
For instance, if you use NSMutableArray +array class method to instanciate the array object instead of +alloc and -init method,
NSMutableArray *array = [NSMutableArray array];
// NSMutableArray *array = [[NSMutableArray alloc] init];
It means the array object was registered to Autorelease Pool. So, the reference count of the array object is 2. You can avoid the situation using #autoreleasepool.
do I need to go back a non ARC design
Use release under non-ARC instead of assigning null to the variable under ARC.
what if I wanted to re write the app in Swift
Do exactly the same as Objective-C.

Storing blocks in a c-style array

I am trying to dynamically instantiate a c array of blocks, load it and then run them and could use some help.
// Definitions ===========================================
typedef void (^MorphC)(ScratchC* scratch);
#property (nonatomic) MorphC __strong * morphCs;
// Building up the Morph Registry ========================
static NSMutableDictionary* morphs_;
+ (void) initialize {
morphs_ = [[NSMutableDictionary alloc] init];
[MathC hydrate];
}
+ (void) hydrate {
[MathC registerMorph:#"sin" execute:^(ScratchC* scratch) {
AEScratchPush(scratch, sin(AEScratchPop(scratch)));
}];
}
+ (void) registerMorph:(NSString*)name execute:(MorphC)execute {
[morphs_ setObject:execute forKey:name];
}
+ (MorphC) morphFromKey:(NSString*)key {
return [morphs_ objectForKey:key];
}
// Loading up a temporary NSMutableArray* _compiling =====
- (void) applyTag:(NSString*)tag stack:(Stack*)stack {
[_compiling addObject:[MathC morphFromKey:tag]];
}
// Initializing C Array and loading from NSMutableArray ==
- (void) build {
_morphCs = (MorphC __strong *)malloc(_compiling.count*sizeof(MorphC));
i = 0;
for (MorphC morph in _compiling)
_morphCs[i++] = morph; // Currently, getting a bad ACCESS here
}
// Executing the Morphs ==================================
- (CGFloat) evaluateFloat:(VarsC*)vars {
if (![_morphs count]) return NAN;
AEScratchLoadVariables(_scratch, vars);
for (int i=0;i<[_morphs count];i++)
_morphCs[i](_scratch);
return AEScratchPop(_scratch);
}
I'm currently getting a EXC_BAD_ACCESS while building up the C Array, but I suspect I have a number of issues. I don't totally understand the need for the __strong at the morphCs definition, but the compiler complains with out. Should the property have a strong indicator also?
Do I need to be doing [morph copy] in one or more places?
Is there anything else I'm messing up?
You can't malloc an array of strong pointers.
Think about the semantics of a strong pointer: When it is declared, it's value is initialized to nil. When the variable goes out of scope, it releases its existing value. Therefore, the compiler must be able to keep track of strong pointers to be able to carry this out. If you have an array of strong pointers of unknown length, when it goes out of scope for example, how can the compiler know how many pointers to release? It can't.
In C++ terminology, strong references are "non-POD" types - they have nontrivial constructors and destructors. Therefore, they cannot be allocated with malloc.
It is mentioned here in the ARC specification:
It is undefined behavior if a managed operation is performed on a
__strong or __weak object without a guarantee that it contains a primitive zero bit-pattern, or if the storage for such an object is
freed or reused without the object being first assigned a null
pointer.
In other words, the only way you can use malloc and free is if you guarantee that every time after you call malloc you zero the memory of all the pointers you allocated, before using them. And every time before free you guarantee to first assign nil to each strong pointer in the array.
However, in Objective-C++, you can use new[] and delete[] to dynamically allocate arrays of strong pointers.
These requirements are followed automatically in Objective-C++ when
creating objects of retainable object owner type with new or new[] and
destroying them with delete, delete[], or a pseudo-destructor
expression.

Why is this C array NULL when indirectly called in my Objective-C code? (cocos2d)

i have two Objective-C classes, say ParentLayer and ChildLayer. in my child instance, i want to access a C-Array in my parent instance. so i have something like this in my cocos2d code:
#define kNumOfElements 10
#implementation ParentLayer{
int array[kNumOfElements];
}
-(id)init{
//...
for(int i=0;i<kNumOfElements;i++){
array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
-(int *)getArray{
return array;
}
#end
//meanwhile in my child layer...
//...
-(void)initializeValues{
int *arr = [(ParentLayer *)[self parent] getArray];
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
//...
what's the proper way to do this?
maybe i dont understand the right memory management behind C Arrays.
i was under the impression that C Arrays didn't need to be allocated,
and that they could be passed by value, on the stack?
also, shouldn't my parent instance still be around? i thought if i
put a C Array as an ivar of my parent, it shouldn't get destroyed
any help is appreciated. thanks!
what's the proper way to do this?
Ideally, you should never pass a C-style array pointer outside of the object that owns it. You open yourself up to all sorts of problems if a piece of code tries to use the array after the object is deallocated, or writes past the end, or something else. It is easier to guarantee that none of this happens if you can make sure the reference never leaves the object's source file.
maybe i dont understand the right memory management behind C Arrays. i was under the impression that C Arrays didn't need to be allocated, and that they could be passed by value, on the stack?
It is not that simple.
A C-style array is just a memory address. That's it. It doesn't carry around the other useful information that an object might, such as number of elements, retain count.
If you declare an array like this:
int array[100];
Then the memory is allocated in either the stack or the heap, depending on where you put the declaration. If it's a local variable inside a function or method, it's on the stack. If it's in global scope or a member variable of an object, it's on the heap.
Furthermore, if it's an instance variable, you're actually setting aside 100 ints worth of memory inside the block of memory allocated to hold the object. It isn't a separate thing.
Since array is just a memory address, you are basically passing it around by reference. Technically, you are passing the address by value, but any changes you make to the memory will be seen by anyone looking at the same address, so it acts like pass by reference.
also, shouldn't my parent instance still be around? i thought if i put a C Array as an ivar of my parent, it shouldn't get destroyed
The way you have coded it, that array will be valid as long as the parent object is around. Once the parent gets deallocated, that memory could be reclaimed. Since the array variable is just a memory address, however, you have no way of knowing whether the data it points to is valid or not. This is the danger of using C-style arrays rather than objects.
Since the last line is giving you NULL (0) address, my guess is that [self parent] is nil. That would put a 0 in arr; when you try to dereference NULL, you will get an exception.
In Objective C, you can use property for this.
#define kNumOfElements 10
#interface ParentLayer: NSObject
{
int *array;
}
#property(nonatomic, assign) int *array;
#end
#implementation ParentLayer
-(id)init{
//...
self.array =(int*)malloc(sizeof(int) * kNumOfElements);
for(int i=0;i<kNumOfElements;i++){
self.array[i] = i;
}
[self addChild:childLayer];
[childLayer initializeValues];
//...
}
//-(int *)getArray{
// return array;
//}
-(void)dealloc
{
if(self.array)
{
free(self.array); self.array = NULL;
}
[super dealloc];
}
#end
-(void)initializeValues{
ParentLayer *player = (ParentLayer *)[self parent] ;
int *arr = player.array;
//NSLog(#"%d",arr[0]); <------- this gives you bad exec access point, and looks like it's 0x00 for memory address
}
can't seem to add a reply to benzado's post. but depending on how to declare your object, it might be automatically deallocated. to ensure that it is retained, use a retain keyword.
[obj retain];
especially using the cocos2d framework, they have quite a number of auto release objects. typically initWith shouldn't be auto release.

Object Owns Its Data?

As you can see in the code below I am creating and initialising a Vertex which I then add into a NSMutableArray instance variable within my Frame object. As I currently have this setup myVert is owned by main and pointed to by vertexList. Would I be better setting this up so that I make a copy of inVertex within the addVertex method so the object takes ownership of its data?
-(void)addVertex:(Vertex*) inVertex {
[vertexList addObject:inVertex];
}
// -------------------
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Frame *myFrame;
Vertex *myVert;
NSLog(#"MDD_ObjectTest ... ");
myFrame = [[Frame alloc] init];
myVert = [[Vertex alloc] initWithxPos:111 yPos:222 zPos:333];
[myFrame addVertex:myVert];
[myVert release];
// Clean up
[myFrame release];
[pool drain];
return 0;
}
Finally if I should make a copy what is the best way to do that, should I be looking at ...
-(void)addVertex:(Vertex*) inVertex {
[vertexList addObject:[inVertex copy]];
}
Although I am a little unsure what to do in terms of the Vertex object with regards to copyWith Zone.
gary
This depends on if Vertex is mutable or not.
If Vertex is immutable:
Since the inVertex can not have any of its data changed, the current way you have it is fine. The vertexList will retain inVertex when you add it and will release it when you remove it. Since the properties of inVertex can not be changed there is no difference in storing it versus a copy.
If Vertex is mutable:
You should store a copy so that the changes to inVertex do not affect the vertex stored in the list. Note however that you have a possible memory leak in the way you have it right now. When you copy an object that copy's retain count is set to 1, then when you store it in the vertexList the retain count becomes 2. When you remove it from the list it will have a retain count of 1, causing a memory leak unless you remember to release it. The best place to release it would be in the addVertex method after it is added to the vertexList to keep its retain count at 1, since only one thing has a reference to it.
- (void) addVertex:(Vertex *) inVertex {
Vertex * copy = [inVertex copy];
[vertexList addObject:copy];
[copy release];
}
Note that Vertex must implement the NSCopying protocol for this to work.
I suggest using the immutable approach unless you have a real valid reason to make vertex mutable other than convenience.
Edit: Regarding copyWithZone:
To implement object copying you have to implement the NSCopying protcol that defines the copyWithZone: method. The only consideration you need to make with the zone is that instead of calling the alloc method on the Vertex class you call the allocWithZone: method instead.
- (id) copyWithZone:(NSZone *) zone {
Vertex * copy = [[Vertex allocWithZone:zone] initWithxPos:x yPos:y zPos:z];
// Any other copying that needs to be done
return copy;
}

Array of pointers causes leaks

-(void)setUserFilters{
//init the user filters array
userFilters = [[NSMutableArray alloc] init];
SearchCriteria *tmpSc= [[SearchCriteria alloc] init];
for(int i=0;i<[searchFilters count];i++)
{
tmpSc=[self.searchFilters objectAtIndex:i];
if(tmpSc.enabled==TRUE)
[userFilters addObject:tmpSc];
}
}
searchFilters is a list of filters that can be setted to true or false and I use userFilters to populate a table view with the filters that are only setted to TRUE
But the line SearchCriteria *tmpSc= [[SearchCriteria alloc] init]; causes leaks, and I don't know how to solve because if I release at the end of the function I loose my pointers and it crashes
Any ideas?
twolfe18 has made the code >much slower if searchFilters can be large. -objectAtIndex: is not a fast operation on large arrays, so you shouldn't do it more than you have to. (While true that FE is faster than objectAtIndex:, this overstated the issue and so I've striken it; see my other comments on the advantages of Fast Enumeration.)
There are a number of problems in your code:
Never create a method that begins "set" but is not an accessor. This can lead to very surprising bugs because of how Objective-C provides Key-Value Compliance. Names matter. A property named userFilters should have a getter called -userFilters and a setter called -setUserFilters:. The setter should take the same type that the getter returns. So this method is better called -updateUserFilters to avoid this issue (and to more correctly indicate what it does).
Always use accessors. They will save you all kinds of memory management problems. Your current code will leak the entire array if -setUserFilters is called twice.
Both comments are correct that you don't need to allocate a temporary here. In fact, your best solution is to use Fast Enumeration, which is both very fast and very memory efficient (and the easiest to code).
Pulling it all together, here's what you want to be doing (at least one way to do it, there are many other good solutions, but this one is very simple to understand):
#interface MyObject ()
#property (nonatomic, readwrite, retain) NSMutableArray *userFilters;
#property (nonatomic, readwrite, retain) NSMutableArray *searchFilters;
#end
#implementation MyObject
#synthesize userFilters;
#synthesize searchFilters;
- (void)dealloc
{
[searchFilters release];
serachFilters = nil;
[userFilters release];
userFilters = nil;
[super dealloc];
}
- (void)updateUserFilters
{
//init the user filters array
// The accessor will retain for us and will release the old value if
// we're called a second time
self.userFilters = [NSMutableArray array];
// This is Fast Enumeration
for (SearchCriteria *sc in self.searchFilters)
{
if(sc.enabled)
{
[self.userFilters addObject:sc];
}
}
}
It seems that your initially creating a SearchCriteria object and before you use it or release it your reassigning the variable to another object from self.searchFilters. So you don't need to create the initial object and why it's leaking and not being released.
Try:
SearchCriteria *tmpSc = nil;
Hope that helps.
first of all, the worst n00b code you can write involves if(condition==true) do_something(), just write if(condition) do_something().
second, there is no reason to have tempSc at all (never mind alloc memory for it), you can just do the following:
-(void)setUserFilters{
//init the user filters array
userFilters = [[NSMutableArray alloc] init];
for(int i=0;i<[searchFilters count];i++)
{
if([self.searchFilters objectAtIndex:i].enabled)
[userFilters addObject:[self.searchFilters objectAtIndex:i]];
}
}