Cocoa的命名规则对于assign初始化器是什么?

4

由于某种原因,我认为这在内存管理命名规则下是有效的:

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

然而,现在我正在运行静态分析时,它认为每次我这样做都存在潜在泄漏。

我看到有__attribute((ns_consumed))可以用在fooWithNewBar的声明上。但是Xcode 4.0.1的Clang还不支持这个属性。

那么,没有这样的命名模式吗?


感谢所有回答过并解释了 Cocoa 中基本内存管理的规则的人。但这不是我想要知道的。我想知道在初始化程序中是否有一种可接受的所有权转移模式。我的理由是出于风格上的考虑。我试图节省代码库中已经太长且非常繁琐的行中 autorelease 的重复输入,并且重新编写 Bar 以使用 +(id)className 初始化程序并不容易,因为它有50个子类。 - mxcl
烦人的微妙之处:如果 Foonil,它就不能拥有所有权!对于实例方法,这是相当明显的;对于类方法,如果你在 iOS 6 中使用类但在 iOS 5 上运行,则可能会发生这种情况。这不是一件通常安全的事情;我会避免这样做。 - tc.
5个回答

5
我认为在你的情况下没有与ns_consumed相对应的命名模式。命名模式主要受NeXTSTEP/Apple驱动,我想不到苹果框架中有与您所需语义相同的方法。
请注意,但是,您可以告诉Xcode使用更高版本的Clang静态分析器,该分析器支持ns_consumed属性,该属性已发布checker-254
我正在使用checker-256(今天发布,但任何版本>= 254都应该可以),并尝试了以下操作:
// 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

并且

// 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

这段代码会触发静态分析器警告,因为存储在 s 中的字符串可能会泄漏:
// SomewhereElse.m
NSString *s = [[NSString alloc] initWithFormat:@"%d",
    [[NSProcessInfo processInfo] processIdentifier]];
MyClass *o = [MyClass myClassWithNewString:s];
[o release];

而这段代码使用了带有ns_consumed属性的方法参数,不会给静态分析器警告:

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

1

没有这样的模式。无论我在哪里看到它,我都没有。


如果您正在尝试将此用于“assign”属性,则仍需要在代码的某个时刻释放您创建的对象。 - kubi
是的,我并不打算将其用于“assign”属性,我只是用它来举例说明我希望“Foo”初始化程序如何处理接收部分以获取“Bar”实例。 - mxcl

0
Bar *bar = [Bar new];

你拥有酒吧。

[Foo fooWithNewBar:bar];

如果想要拥有 bar,就应该保留它。这并不放弃你的所有权,所以你仍然需要释放 bar。

我从未见过你使用的这种模式。


我正在寻找一种命名模式,以便将所有权转移,就好像它后来被分配给了一个(assign)属性。我理解你所描述的Cocoa内存所有权规则。 - mxcl

-1
在Objective-C引用计数内存管理中,所有权是C++中的share_ptr,而不是auto_ptr风格。换句话说,您不会将所有权“转移”给Foo实例,而是Foo实例在您的Bar实例中声明了一个所有权共享(同时留下了您作为[Bar new]调用者也拥有份额的所有权共享)。您必须在实例被释放之前放弃对其所有权份额的所有权。因此,您的示例应该是这样的:

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

请注意,您必须使用-autorelease而不是-release来放弃您的所有权份额,因为在bar被释放之前,Foo需要有机会声明所有权份额。

-1

Foo 对 Bar 产生了兴趣,但是 *bar 在 [bar release] 之前仍然保持着这种兴趣。


2
我认为你没有理解这个问题。 - mxcl
new只是alloc/init的替代品。 - Black Frog
5
我仍然认为你不理解这个问题。 - mxcl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接