5

除了通过 3rd 方代码强制它进入项目时,我还没有使用 ARC 来处理它。我已经阅读了所有 ARC 文档,但还没有看到这个问题的答案:

如果我有一个在用 编译的模块中定义的类,-fobjc-arc我可以在不支持 ARC 的模块中从中派生一个新类吗?

在我看来,只要派生类不尝试触及根类中的任何 ivars,它就应该可以正常工作。在我看来,即使[super dealloc]在派生类中有一个可以调用的 dealloc 方法也可以。

而且,反过来呢?我可以从非 ARC 类派生启用 ARC 的类吗?也应该可以正常工作,对吧?

加分点:在混合 ARC 和非 ARC 代码时,我应该让自己意识到什么问题?

4

4 回答 4

6

没有我知道的问题。您必须意识到 ARC 类似于源代码预处理器,在编译期间为您添加内存管理调用。当您到达链接阶段时,您无法真正区分 ARC 代码和非 ARC 代码。(这可能是一种过度简化,但应该适用于您的目的。)如果您的派生类具有正确的内存管理并且超类具有正确的内存管理,则结果将正常工作。

关于我能想到的唯一区别是处理weak属性。但是我不太了解那些说是否有可能使用具有弱属性的 ARC 和 MRC 代码的某种组合来得出有缺陷的代码。

于 2012-06-13T15:36:43.490 回答
0

这是一条评论,但考虑到它,我想扩展它所说的内容。

您是否尝试过从普通子类继承 ARC 类?我的想法(也没有尝试过)是这行不通。首先,如果 ARC 类具有使用 ARC 关键字的公共属性或 ivars,就像weak我认为在从头文件编译时会出错。其次,我不知道它dealloc会如何工作。你需要打电话[super dealloc]还是不打电话?我不知道。

无论如何,如果您的超类是 ARC,为什么不在任何子类中使用 ARC?这样做根本没有任何优势。

我可以从非 ARC 类派生启用 ARC 的类吗?也应该可以正常工作,对吧?

我本来想说那也行不通,但我错了。几乎所有东西都必须继承NSObject手动引用计数。

于 2012-06-13T15:36:20.383 回答
0

是的,您既可以从 ARC 父类实现非 ARC 祖先,也可以从非 ARC 父类实现 ARC 祖先。

实际上,ARC 是一种语法糖,或者您可以说,它只是在编译步骤分析您的源代码并在您的代码中插入适当的 [release] 和 [retain] 调用的预处理器。在运行时级别没有任何改变(除了weak属性)。

于 2012-06-13T16:06:17.533 回答
0

ARC 意味着编译器负责内存管理,非 ARC 意味着你负责它,但在这两种情况下内存管理的工作方式完全相同:

  • 如果一个对象必须保持活动状态,它的保留计数器会增加(这就是这样retain做的)
  • 如果不再需要一个对象,它的保留计数器会在对它的引用丢失之前减少(这就是这样release做的)
  • 如果你完成了一个对象但它还不能死,例如你需要将它作为方法结果返回(并且你不想返回一个死对象),它必须被添加到一个自动释放池中,这将减少它在以后的时间保留计数(就是autorelease这样,就像说“release在未来某个时间调用该对象。”)
  • 新创建的对象的保留计数为1.
  • 如果保留计数达到零,则释放对象。

无论您是自己做所有这些,还是编译器为您做这一切,它都不起作用。编译后,这些方法会被调用,ARC 也是如此,但使用 ARC 时,编译器会为您决定何时调用哪个方法。还有一些额外的魔力,例如 ARC 在将对象作为方法结果返回时并不总是必须将对象添加到自动释放池中,这通常可以被优化掉,但你不必关心,因为这种魔力仅在调用者时应用并且被调用的方法都使用ARC;如果其中一个不是,则使用法线autorelease(它仍然像以前一样在 ARC 中工作)。

您唯一需要注意的是保留周期。无论您是否使用 ARC,引用计数都无法处理保留周期。这里没有区别。

陷阱?小心使用免费桥接。ANSString *和 aCFStringRef实际上是一回事,但 ARC 不知道 CF 世界,所以当 ARC 处理 时NSString,您必须处理CFString. 使用 ARC 时,需要告诉 ARC 如何桥接。

CFStringRef cfstr = ...;
NSString * nsstr = (__bridge_transfer NSString *)cfstr;
// NSString * nsstr = [(NSString *)cfstr autorelease];

上面的代码意味着“ARC,请取得该CFString对象的所有权,并在完成后立即释放它”。该代码的行为类似于下面评论中显示的代码;非常小心,cfstr应该至少保留一个计数,ARC 将至少释放一次,只是还没有。反过来:

NSString * nsstr = ...;
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr;
// CFStringRef cfstr = (CFStringRef)[nsstr retain];

上面的代码意味着“ARC,请给我它的所有权,NSString一旦我完成它,我会负责发布它”。当然,你必须信守诺言!在某些时候你将不得不打电话,CFRelease(cfstr)否则你会泄漏内存。

最后,(__bridge ...)这只是一个类型转换,没有所有权转移。这种类型的转换是危险的,因为如果你试图保留转换结果,它会产生悬空指针。通常,当您将 ARC 对象提供给期望 CF 对象的函数时,通常会使用它,因为 ARC 肯定会在函数返回之前使对象保持活动状态,例如,这始终是安全的:

doSomethingWithString((__bridge CFStringRef)nsstr); 

即使 ARC 被允许nsstr在任何时候释放,因为该行下面的代码不再访问它,它肯定不会在此函数返回之前释放它,并且根据定义,函数参数仅保证在函数返回之前保持活动状态(以防万一该函数想要保持字符串处于活动状态,它必须保留它,然后 ARC 在释放它后不会释放它,因为保留计数不会变为零)。

大多数人似乎难以解决的问题是将 ARC 对象作为void *上下文传递,就像您有时必须使用旧 API 一样,但这实际上非常简单:

- (void)doIt {
   NSDictionary myCallbackContext = ...;
   [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
        context:(__bridge_retained void *)myCallbackContext
    ];
    // Bridge cast above makes sure that ARC won't kill
    // myCallbackContext prior to returning from this method.
    // Think of:
    // [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
    //    context:(void *)[myCallbackContext retain]
    // ];
}

// ...

- (void)iAmDone:(void *)context {
    NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context;
    // Use contextDict as you you like, ARC will release it
    // prior to returning from this method. Think of:
    // NSDictionary * contextDict = [(NSDictionary *)context autorelease];
}

而且我必须对你来说真正的大问题,乍一看并不那么明显。请考虑以下代码:

@implementation SomeObject {
    id _someIVAR;
}

- (void)someMethod {
    id someValue = ...;
    _someIVAR = someValue;
}

此代码在 ARC 和非 ARC 中是不一样的。在 ARC 中,默认情况下所有变量都是强变量,因此在 ARC 中,这段代码的行为就像这段代码一样:

@interface SomeObject
    @property (retain,nonatomic) id someIVAR;
@end

@implementation SomeObject

- (void)someMethod {
    id someValue = ...;
    self.someIVAR = someValue;
}

分配someValue将保留它,对象保持活动状态!在非 ARC 中,代码的行为如下:

@interface SomeObject
    @property (assign,nonatomic) id someIVAR;
@end

@implementation SomeObject

- (void)someMethod {
    id someValue = ...;
    self.someIVAR = someValue;
}

请注意属性是不同的,因为非 ARC 中的 ivar 既不是strongor weak,它们什么也不是,它们只是指针(在被调用的 ARC 中,__unsafe_unretained这里的关键字是unsafe)。

因此,如果您的代码直接使用 ivars 并且不使用带有 setter/getter 的属性来访问它们,那么从非 ARC 切换到 ARC 可能会导致过去具有健全内存管理的代码中的保留周期。另一方面,从 ARC 迁移到非 ARC,这样的代码可能会导致悬空指针(指向以前的对象的指针,但由于对象已经死亡,这些指针指向无处并且使用它们会产生不可预知的结果),因为过去的对象以前还活着,现在可能意外死去。

于 2017-05-23T18:59:20.830 回答