views:

175

answers:

6

I have this snippet of code from a class created by Erica Sadun, that Instruments says is leaking:

- (void)cacheBeginPointForTouches:(NSSet *)touches
{
    if ([touches count] > 0) {
        for (UITouch *touch in touches) {
            CGPoint *point = (CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch);
            if (point == NULL) {
                point = (CGPoint *)malloc(sizeof(CGPoint));
                CFDictionarySetValue(touchBeginPoints, touch, point);
            }
            *point = [touch locationInView:self.superview];
        }
    }
}

Instruments is pointing to

 point = (CGPoint *)malloc(sizeof(CGPoint));

as the leaking line.

As this malloc stuff is not familiar to me. I know that it allocates memory, but as I never worked with C, C++ and other flavors of C, malloc and I are not acquaintances.

Another question I don't understand is why she put an asterisk before "point" on

*point = [touch locationInView:self.superview];

So, do you see something wrong with the code and why instruments are saying it is leaking there? An explanation about the asterisk is a bonus! :)

thanks.

+1  A: 

Without knowing the context, I'm guessing the compiler doesn't like that the there is no check to see whether or not malloc fails (malloc = memory allocation, it grabs available memory and assigns it to your program variable). Also, this snippet by itself does not free the malloc'd memory (although I'm assuming this is done elsewhere).

Also, the "*" dereferences the value. For example, in standard C, if I write:

 int val = 1;
 int *p = &val;
 printf("%d\n",*p);
 *p = 2;
 printf("%d\n",*p);

I'd get:

 1
 2

The * allows you to reference the object to which the pointer is pointing. In the example above, "p" is a pointer, but "*p" is the actual value the pointer is referencing ("val").

Andrew
thanks, but so, how should this line be written? Sorry but as I said, my knowledge about malloc is zero, niet, nada, nothing!
Digital Robot
There's no simple solution ... after the malloc, you'd have something like "if (point == null){/* do something */ }", the problem is that the something is some type of application specific error handling.
Andrew
+3  A: 

The code snippet is leaking, because it is not complete. What it does is to allocate memory for a CGPoint object. This has to be free'd when it is no longer in use. You have omitted that part.

In this case, the CGPoint object seems to be used in the CFDictionary. So there should be code to determine when it is safe to free it again.

relet
I have not omitted anything. This is the whole method. The only thing that appears to be releasing the object is on another method present on the class, but anyway Xcode is still pointing its fingers and cursing the line I mentioned.
Digital Robot
Yes. A common point to release your memory would be in the destructor of a class (or a method called by that destructor). In this case, it could iterate over the CFDictionary and release the memory of all entries, wherever they have been allocated.If you would release the memory in this method, the entry that has been set in the dictionary would become corrupted.
relet
+5  A: 

The rules for malloc are quite simple. Once you're done with the memory, you should free it, using free(pointer). So at some point in your code, the dictionary will be used to get the CGPoints. If your program does nothing after this with the CGPoints (and the pointer is removed from the dictionary), you should call free(point) on them.

The line

*point = ...;

means to say: put ... in the location in memory, pointed to by point. The dictionary contains these pointers to your CGPoint values, and as you see you can easily first store the pointer, and only then fill the memory pointed to (although, I must admit, this is not very intuitive)

mvds
+1  A: 

malloc() and free() are like [NSObject alloc] and [NSObject release] in Objective-C. malloc() allocates memory and returns a pointer. free() tells the OS that the memory is no longer needed. A malloc() without a free() is then, by definition, a memory leak.

It's not clear here whether there is an actual memory leak, since the pointer is being stored in a dictionary. When the dictionary is destroyed, or when the value for that key is overwritten, the memory must be freed.


As for the line:

*point = [touch locationInView:self.superview];

It takes the return value of locationInView: and stores it at the address pointed to by point. The "*" there is the dereferencing operator.

Seamus Campbell
thanks for the explanation!
Digital Robot
+2  A: 

At a guess, you have probably not created the dictionary properly. A regular dictionary thinks you'll be using the CFRetain/CFRelease pattern to hold on to objects - however, you can attach alternate handlers that use your own memory mamagement scheme.

You should check the CFDictionaryNew() call in your code and make sure it matches the one in Erica's. I suspect she has a custom value for valueCallbacks while you probably don't.

Jeff Laing
thanks. I will check that.
Digital Robot
A: 

I am purely guessing here, given that the function is named "cache"BeginPointForTouches, it would appear that the purpose of this function is to create a dictionary of touches and cache them for fast lookup. Being a cache, it looks like it is the kind that keeps its memory around throughout the lifetime of the program and never frees itself. So to make sure, does this leak happen during the program or after the program exits? If after, then what I described is likely and isn't really a leak.

5ound
it happens during the program execution. ,
Digital Robot