[英]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.