2012-06-13 69 views
5

除了在通过第三方代码强制进入项目时处理它,我还没有使用过ARC。我读过所有的ARC文档,但没有看到这个问题的答案:ARC,非ARC和继承

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

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

而且,另一种方式呢?我可以从非ARC类派生一个启用ARC的类吗?应该工作得很好,对吗?

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

回答

6

我没有意识到任何问题。你必须认识到ARC就像是一个源代码预处理器,在编译过程中为你添加内存管理调用。当你到达链接阶段时,你不能真正告诉非ARC代码的ARC代码。 (这可能是一种过度简化,但应该适合您的目的。)如果您的派生类具有正确的内存管理,并且超类具有正确的内存管理,则结果将正常工作。

关于我能想到的唯一区别是处理weak属性。但是我不太了解那些可以使用ARC和MRC代码与弱属性的组合来得到错误代码的人。

+0

这似乎是共识。谢谢你的帮助。 – TomSwift

0

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

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

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

我可以从非ARC类派生一个支持ARC的类吗?应该工作得很好,对吗?

我想说这也行不通,但我会错的。几乎所有的东西都必须从手动引用计数的NSObject继承。

+0

从MRC代码中启用ARC的类派生出来是有道理的,例如,当您使用ARC进行链接并且不能重写主项目时。 (我这样做,它似乎工作。)你不能在ARC下调用'[super dealloc]',我认为编译器会为你插入调用。 – zoul

+0

@zoul - 是的,这也是我们的情况;第三方模块需要ARC,但我们不准备将ARC用于我们自己的模块。 – TomSwift

+0

拥有ARC类的非ARC子类也是有意义的。特别是,如果一个子类必须大量使用CF API,那么从MRR代码比ARC代码更容易实现。 – bbum

0

是的,你既可以实现ARC父类的非ARC祖先,也可以实现非ARC父类的ARC祖先。实际上,ARC是一种语法糖,或者你可以说,它只是预处理器,它在编译步骤中分析你的源代码,并向你的代码插入适当的[释放]和[保留]调用。在运行时级别上,没有任何更改(weak属性除外)。

0

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

  • 如果一个对象必须活下去,其保留计数器增加(这是retain一样)
  • 如果不再需要一个对象,它是失去了保留计数器被引用之前下降(这就是release一样)
  • 如果您正在使用对象来完成,但它一定不会死亡,例如因为您需要将其作为方法结果返回(并且您不想返回死对象),所以它必须添加到autorelease池中,以后将减少其保留计数(这就是autorelease所做的,就像说“在将来某个时间在该对象上调用release”。)
  • 新创建的对象的保留计数为1
  • 如果保留计数达到零,则释放该对象。

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

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

陷阱?小心免费桥接。一个NSString *和一个CFStringRef事实上是同样的事情,但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既不strongweak,它们是什么,他们只是指针(在ARC是称为__unsafe_unretained,此处的关键字是不安全)。

所以,如果你有直接使用ivars的代码,并且没有使用setter/getters的属性来访问它们,那么从非ARC到ARC的切换可能导致代码中的保留循环过去具有理智的内存管理。另一方面,从ARC移动到非ARC,类似这样的代码会导致悬挂指针(指向前一个对象的指针,但由于对象已经死亡,这些指向无处并且使用它们具有不可预知的结果),因为过去的对象在现在可能意外死亡之前保持活力。