4

For some reason I thought this was valid under the memory management naming rules:

Bar *bar = [Bar new];
[Foo fooWithNewBar:bar];
// no need to release bar, foo took ownership

However now I'm running my static analysis, it thinks there's a potential leak everytime I did this.

I see there is __attribute((ns_consumed)) which I could use on the fooWithNewBar declaration. But Xcode 4.0.1's Clang does not yet support this attribute.

So, there's no such naming pattern?

mxcl
  • 26,392
  • 12
  • 99
  • 98
  • Thanks everyone who answered so far and described *basic* memory management in Cocoa and the rules of it. I didn't ask for any of that. I wanted to know if there was an acceptable pattern for transferring ownership in an initializer. My reasons for this are stylistic. I am trying to save repeated typing of autorelease in lines that are already too long and very numerous in this codebase, and rewriting Bar to use `+(id)className` initializers is not an easy undertaking as it has 50 subclasses. – mxcl Apr 13 '11 at 17:02
  • Annoying subtlety: if `Foo` is `nil`, it *cannot* take ownership! For instance methods, this is fairly obvious; for class methods, this might happen if you're e.g. using a class in iOS 6 but running on iOS 5. It's not a generally safe thing to do; I'd avoid it. – tc. Jun 07 '13 at 19:38

5 Answers5

5

I also think there’s no naming pattern corresponding to ns_consumed in your case. Naming patterns are largely driven by NeXTSTEP/Apple and I can’t think of a method in Apple’s frameworks with the same semantics you want.

Note, however, that you can tell Xcode to use a more recent version of Clang Static Analyser that supports the ns_consumed attribute, which was released with checker-254.

I’m using checker-256 (released today, but any version >= 254 should work) and I’ve just tried the following:

// MyClass.h
#ifndef __has_feature      // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif

#ifndef NS_CONSUMED
#if __has_feature(attribute_ns_consumed)
#define NS_CONSUMED __attribute__((ns_consumed))
#else
#define NS_CONSUMED
#endif
#endif

@interface MyClass : NSObject {
@private
    NSString *_string;
}
+ (MyClass *)myClassWithNewStringConsumed:(NSString *) NS_CONSUMED string NS_RETURNS_RETAINED;
+ (MyClass *)myClassWithNewString:(NSString *)string NS_RETURNS_RETAINED;
@end

and

// MyClass.m
#import "MyClass.h"

@implementation MyClass

+ (MyClass *)myClassWithNewStringConsumed:(NSString *)string {
    MyClass *o = [MyClass new];
    if (o) o->_string = string;
    return o;
}

+ (MyClass *)myClassWithNewString:(NSString *)string {
    MyClass *o = [MyClass new];
    if (o) o->_string = string;
    return o;
}

@end

This code gives a static analyser warning for a potential leak of the string stored in s:

// SomewhereElse.m
NSString *s = [[NSString alloc] initWithFormat:@"%d",
    [[NSProcessInfo processInfo] processIdentifier]];
MyClass *o = [MyClass myClassWithNewString:s];
[o release];

whereas this code, which uses the method parameter with an ns_consumed attribute, doesn’t give a static analyser warning:

// SomewhereElse.m
NSString *s = [[NSString alloc] initWithFormat:@"%d",
    [[NSProcessInfo processInfo] processIdentifier]];
MyClass *o = [MyClass myClassWithNewStringConsumed:s];
[o release];
1

There is no such pattern. Wherever I think I read about it, I didn't.

mxcl
  • 26,392
  • 12
  • 99
  • 98
  • If you're trying to use this for `assign` properties, you still need to release the object you created at some point in your code. – kubi Apr 13 '11 at 15:05
  • Yeah, I'm not trying to use this for `assign` properties, I was just using that as example of how I wanted the `Foo` initializer to deal with the receiving portion of taking the `Bar` instance. – mxcl Apr 13 '11 at 17:04
0
Bar *bar = [Bar new];

You own bar.

[Foo fooWithNewBar:bar];

If this wants to own bar, it should be retaining it. That doesn't relinquish your ownership, so you still have to release bar.

I've never seen the pattern you're using.

Terry Wilcox
  • 9,010
  • 1
  • 34
  • 36
  • I'm looking for a naming pattern that transfers ownership, as though it was assigned to a (assign) property later. I understand the Cocoa memory ownership rules that you describe. – mxcl Apr 13 '11 at 14:56
-1

Foo took an interest in Bar, but *bar still has interest until [bar release] that interest.

Black Frog
  • 11,595
  • 1
  • 35
  • 66
-1

Ownership in Objective-C reference-counted memory management is C++ share_ptr, rather than auto_ptr style. In other words, you don't transfer ownership to the Foo instance. Rather the Foo instance declares a share of ownerhsip in your Bar instance (leaving you, the caller of [Bar new] with a share of ownership as well). You always need to relinquish your ownership share of an instance before it may be deallocated. Thus, your example should be

Bar *bar = [[Bar alloc] init]; //Most Obj-C devs prefer alloc/init. When in Rome...
[Foo fooWithNewBar:[bar autorelease]]; //relinquish ownership of bar

Note that you have to use -autorelease rather than -release to relinquish your ownership share because Foo needs a chance to claim an ownership stake before bar is deallocated.

Barry Wark
  • 107,306
  • 24
  • 181
  • 206