我应该释放nonnull属性吗?如果是这样,如何释放?

4
我正在将一个Objective-C项目的一些属性暴露给Swift(基于this repo),但是我没有Objective-C的经验,所以在这里我有点力不从心,请您谅解。
我想知道如何正确释放一个nonnull属性(或者是否需要释放!)。我已经通过将其设置为null(与对可空partOfSpeech所做的操作相同)来暂时dealloc非null属性surface。然而,这会提示以下警告:
Null passed to a callee that requires a non-null argument
因此我想知道它是否多余。在Node类的dealloc块期间,有没有其他处理nonnull属性的方法?
给出了接口node.h:
@interface Node : NSObject {
    NSString *surface;
    NSString *partOfSpeech;
}

@property (nonatomic, retain, nonnull) NSString *surface;
@property (nonatomic, retain, nullable) NSString *partOfSpeech;

- (nullable NSString *)partOfSpeech;

@end

...而且实现部分,node.m

@implementation Node

@synthesize surface;
@synthesize partOfSpeech;

// surface is assumed to be set post-initialisation.

- (void)setPartOfSpeech:(NSString *)value {
    if (partOfSpeech) [partOfSpeech release];
    partOfSpeech = value ? [value retain] : nil;
}

- (NSString *)partOfSpeech {
    if (!features || [features count] < 1) return nil;
    return [features objectAtIndex:0];
}

- (void)dealloc {
    // WARNING: "Null passed to a callee that requires a non-null argument"
    self.surface = nil;
    self.partOfSpeech = nil;
    [super dealloc];
}

@end

“...而且考虑到一个 Node 的生命周期是这样的:”
    Node *newNode = [Node new];
    newNode.surface = [[[NSString alloc] initWithBytes:node->surface length:node->length encoding:NSUTF8StringEncoding] autorelease];
   // ... Do stuff with newNode (eg. add to array of Node)...
    [newNode release];

@Ron 感谢你指出这一点。这是一个 Obj-C 包装 C++ 项目的代码,(最终将头文件暴露给 Swift!),所以我有些混淆了。正如你所说,这里呈现的代码纯粹是 Obj-C。 - Jamie Birch
不使用ARC(自动引用计数)有特殊原因吗? - Martin R
@MartinR 由于这是一个高性能、行业标准的标记器包装器,可能有一些争议需要对内存管理进行严格、明确的控制。但我认为,在实践中,Xcode会覆盖内存管理以使用ARC(如果我理解正确的话)。至于我的“特殊原因”,我会说,如果没有更多的理由,“作为学习练习”。 - Jamie Birch
2个回答

3

首先:编译器可以自动合成实例变量和属性的setter/getter方法。因此,你的接口应该只包含属性声明。

// Node.h
@interface Node : NSObject

@property (nonatomic, retain, nonnull) NSString *surface;
@property (nonatomic, retain, nullable) NSString *partOfSpeech;

@end

在实现文件中不需要使用@synthesize语句。编译器会自动创建实例变量_surface_partOfSpeech,并创建访问器方法。

- (NSString *) surface;
- (void)setSurface:(NSString *)value;
- (NSString *)partOfSpeech;
- (void)setPartOfSpeech:(NSString *)value;

无论使用ARC还是不使用ARC,都可以实现"正确的事情"。如果您想要实现一些自定义逻辑,则可以覆盖这些方法,但您不必像setPartOfSpeech那样实现标准setter。

如果您使用ARC(自动引用计数),那么就没有更多需要了。我真的建议您这样做。编译器会在编译时插入所需的保留/释放调用,并且非常聪明地避免不必要的调用。例如,请参见

关于一些比较。使用MRC(手动引用计数),您的代码甚至可能会更慢或存在内存泄漏。

但是回答您的问题:使用MRC,您必须在dealloc中释放实例变量。

- (void)dealloc {
    [_surface release];
    [_partOfSpeech release];
    [super dealloc];
}

高级内存管理编程指南中所述,本文涉及编程相关内容。

dealloc中,您不应使用访问器方法。

self.surface = nil;
self.partOfSpeech = nil;

请查看不要在初始化方法和dealloc方法中使用访问器方法


我认为在你最后一段中,你的意思是不应该使用访问器方法,正如你所引用的参考资料所述。 - CRD

1
如果您正在使用手动内存管理,只需释放存储在属性后备变量中的对象即可。由于您将后备变量命名为与属性相同,因此请使用->清晰地引用后备变量:
[self->surface release];

或者如果您想通过赋值来实现这个目标,只需将空字符串文本分配即可:

self.surface = @"";

字符串字面量在编译时创建,贯穿整个程序执行过程,占用非常少的空间。赋值会导致属性中先前的值被“释放”(如果引用计数达到零,则进行解除分配),就像分配“nil”(或任何其他值)一样。
希望有所帮助。

这两个代码片段应该放在哪里?它们会替换我当前放置的 self.surface = nil; 吗? - Jamie Birch

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