Search code examples
objective-cmemory-leaksclang-static-analyzer

How do I prevent certain kinds of analyzer leak reports?


I am wrapping a certain C API in Objective-C. I have a convenience method that takes some CFTypeRef from the procedural API and returns a wrapping object from the OOP API. This object keeps the passed CFTypeRef and releases it upon its own deallocation. The convenience method looks like this:

+ (id) wrapFoo: (CFTypeRef) foo;

I have a lot of methods that simply get some CFTypeRef and return the wrapping object:

- (id) doSomething {
    CFTypeRef foo = CFCreateSomeObject();
    id wrapper = [WrappingClass wrapFoo:foo];
    CFRelease(foo);
    return wrapper;
}

This is a bit clumsy, so that I came up with another convenience method:

+ (id) wrapNonRetainedFoo: (CFTypeRef) foo {
    id wrapper = [self wrapFoo:foo]; // CFRetains foo
    CFRelease(foo);
    return wrapper;
}

Now I can rewrite the doSomething method like this:

- (id) doSomething {
    return [WrappingClass wrapNonRetainedFoo:CFCreateSomeObject()]; // XXX
}

I like this. I’m not really proud of the wrapNonRetainedFoo method, but it’s not a part of the public interface of the package and saves me several lines of boilerplate code in several methods.

The downside is that the static analyzer flags the XXX line as a potential leak. What can I do better? I tried to toy with the cf_consumed argument attribute to let the analyzer know that I’m releasing the object later, but it does not seem to work.


Solution

  • 1) AFAIK cf_consumed is still not supported in versions of analyzer Apple uses.

    2) I've noticed that if you make wrapNonRetainedFoo instance method warning will mysteriously disappear. But since wrap... is better to be a class method this is of no use to us.

    3) Only solution I can think of is this ugly macro (not for production, just as proof of concept):

    #define WRAP_CFTYPE(klass, valExpr) ({ CFTypeRef val = valExpr; id result = [klass wrap:val]; CFRelease(val); result; })
    

    Usage:

    WrappingClass *wrapper = WRAP_CFTYPE(WrappingClass, CFArrayCreate(NULL, NULL, 0, NULL))