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;
}
Related
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.
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.
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.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this :
classA
-(NSMutableArray*)set:(NSMutableArray*)data
{
manch= [[data mutableCopy]autorelease] ;
int count=2*[data count]; //to not enter infinity loop
for(int k=0;k< count;k=k+2)
{
if(k==count-1)
[manch addObject:[NSNumber numberWithInt:![[manch objectAtIndex:k] integerValue] ] ];
}
data=[manch mutuableCopy];
return data;
}
My goal is to create a class that gets an NSMutuableArray do some calculations, than return it, and NOT TO BE DEPEND on this class anymore .
EDIT :
As people here ask.
in another classB(the user of the method above), i have in the interface :
NSMutuableArray *data ;
and on the .m file init method i have
data=[[NSMutuableArray alloc]init];
to use the function from my question, i do :
mIns=[[classA alloc]init];
data= [mIns set:[self decimalToBinary:autoWord]];
than i loose data later.
I dont want to return manch because if i autorelease before i return it ,it becomes invalid to others. so i was thinking of this:
This is an incorrect statement, you can return an autoreleased object, that's a sane thing to do. It's worth noting that you should design your method names correctly to inform the user what sort of object is returned. Any method whose name begins with alloc, new, copy, or mutableCopy will return a retained object. (Source)
In your case, your method name is set:, which informs the user of this method that it will return a non retained object (almost always an autoreleased object). This is because it isn't prefixed with any of those words mentioned above.
In that case, the issue you have is with the user of the method; they are not retaining a reference to the object being returned. As such, the user of the method should use it as so:
#interface ClassName () {
NSMutableArray* m_ivarArray;
}
#property (nonatomic, retain) NSMutableArray* propertyArray;
#end
NSMutableArray* data = ...;
// If using a property with retain, setting via "self." will retain it for you
self.propertyArray = [self set:data];
// If using an ivar (which doesn't do the retain for you)
m_ivarArray = [[self set:data] retain];
You can avoid these issues by using Automatic Reference Counting (ARC, More Information), which will handle this sort of memory management for you. It is still important that you use the correct naming conventions, as ARC will judge how to manage your memory based on this (in certain situations)
Update: After seeing your update, I can see the problem.
data=[[NSMutuableArray alloc]init];
This is creating a new instance of NSMutableArray, one which is correctly retained (due to what I mentioned before).
data= [mIns set:[self decimalToBinary:autoWord]];
This is replacing the object held in data with a new NSMutableArray, one that is autoreleased. The previous instance you created has been lost, and you've replaced it with another one. This new instance has not been retained, and as such, will be released unexpectedly.
To fix, you need to use this instead:
NSMutableArray* data = [[mIns set:[self decimalToBinary:autoWord]] retain];
You don't need to alloc/init a variable if it will be populated by some other object later on. I strongly suggest brushing up on how this all works, this might be a good start.
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.