繁体   English   中英

ARC,非ARC和继承

[英]ARC, non-ARC, and inheritance

当它通过第三方代码将其强制进入项目时,除了处理它外,我还没有使用过ARC。 我已经阅读了所有ARC文档,但没有看到以下问题的答案:

如果我有一个用-fobjc-arc编译的模块中定义的类,是否可以在未启用ARC的模块中从中派生一个新类?

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

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

优点:在混合ARC和非ARC代码时,我应该意识到自己的陷阱吗?

我没有发现任何问题。 您必须意识到ARC就像源代码预处理器一样,它在编译期间为您添加了内存管理调用。 进入链接阶段时,您实际上无法从非ARC代码中分辨出ARC代码。 (这可能是一种过度简化,但应该可以满足您的目的。)如果派生类具有正确的内存管理,而父类具有正确的内存管理,则结果将正常工作。

我能想到的唯一区别是处理weak属性。 但是我对这些还不十分了解,无法说出使用性能较弱的ARC和MRC代码的某种组合来得出错误代码的可能性。

这是一条评论,但是考虑到这一点,我想扩大它的意思。

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

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

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

我当时想说那也不行,但是我会错的。 几乎所有内容都必须从NSObject继承,而NSObject是手动引用计数。

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

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

ARC表示编译器负责内存管理,非ARC表示您负责内存管理,但是在两种情况下,内存管理的工作方式完全相同:

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

无论您是自己做所有事情,还是编译器为您做这些,它都不起作用。 编译后,也会使用ARC调用这些方法,但是使用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的保留数至少应为1,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将不会取消分配它,因为保留计数不会为零)。

就像您有时使用较旧的API时一样,大多数人似乎在挣扎的是将ARC对象作为void *上下文传递,但这实际上非常简单:

- (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既不是strong也不是weak ,它们什么也不是,它们只是指针(在ARC中称为__unsafe_unretained ,此处的关键字为unsafe )。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM