在函数范围之外保持 objectiveC 对象有效

Keeping objectiveC object valid outside the scope of a function

我对 ARC 在设置作为输入指针的变量时的行为感到有点困惑,并且期望在函数范围之外保持有效。

考虑以下使用 openDirectory 框架的示例。


@interface bbb
-(bool)doSomethingWithADRecord:
-(void)obtainADRecord(NSString*)user
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record;
@end 

@interface bbb {

    ODRecord *_myRecord;
}
@end 


@implementation bbb
-(void)doSomethingWithADRecord:
{
     // here we access _myRecord and expect it to be valid.
}

-(bool)obtainADRecord:(NSString*)user
{
    ...
    // here I call the method that will set the member _myRecord from type ODRecord*
    // whose scope related to the lifespan of the containing class (bbb)
    [self getADrecord:attr toRecord:_myRecord];

}

// the following function should set the variable record to be used by the caller. 
-(NSString*)getADrecord:(ODAttributeType)attr fromRecord:(ODRecord*)record {
    ...
    // here a set an ODQuery object.
    ODQuery *query = [[ODQuery alloc] initWithNode ... 

    // queryResults is an array of items from type ODQuery* 
    NSArray* queryResults = [query resultsAllowingPartial:NO error:&err];

    for(ODRecord *item in queryResults) {
        if (/*some logic*/)
        { 
            //option 1: just regular set operator, expecting the ARC will do the retain itself 
            record = item;

            //option 2: explicits take a reference on that item.  
            record = [[item retain] autorelease];
            return @"found item";
        }
    }
}
@end

为了澄清我的问题,我想知道我上面提到的两个选项中哪一个是正确的,就将引用传递给 record 并最终传递给 _myRecord 而言,所以即使在 queryResults 的临时列表被清除后,它也会存储正确的值。

请注意,在这两个选项中,我只是简单地设置了指针值,而没有从类型 ODquery 启动新对象并将数据复制到这个新对象。

谢谢!

调用 "autorelease" 意味着该对象有一个额外的保留计数,当您离开当前自动释放范围时,该计数将消失,这通常是在当前事件完成时。

record = item 显然是不够的,因为 record 的保留计数在 records 离开作用域时消失,即函数 returns.

但是你所做的 - 为 each 项目调用自动释放确保 all 项目保持分配一段时间,而不仅仅是 "record"。

I'd like to know whether simply doing record = item will be enough for the data pointed by this object to last beyond the scope of the function getADrecord

您误解了参数的工作原理;参数,例如 record,本质上是一个局部变量,它被初始化为调用中传递的 value

因此 任何 将对象引用赋值给 record 将对 getADrecord 范围之外的引用对象的生命周期产生零影响,因为record 是函数局部的。

要通过参数 return 类型 T 的值,参数的类型必须是 "pointer to a variable of type T" 类型。具有简单值类型的示例:

- (void) add:(int)value           // an int value
          to:(int *)ptrToVariable // a pointer to an int variable
{
   // note the need to indirect (`*`) through pointer stored in
   // `ptrToVariable` to access the pointed at variable
   *ptrToVariable = *ptrToVariable + value; 
}

int x = 31;
[self add:11 to:&x]; // &x creates a pointer to the variable x
// x = 42 after call

现在您不想 return 一个简单的值类型,而是一个对对象的引用的值,您希望 ARC 正确管理生命周期。这个有点复杂。

在 ARC 下,保存对象引用的变量同时具有类型和 所有权属性;这个属性告诉 ARC 如何处理变量中存储的引用。共同的所有权属性是 __strong__weak,没有显式属性假定为 __strong。所以你的实例变量声明是 shorthand for:

ODRecord __strong *_myRecord;

此声明意味着对于存储在 _myRecord 中的 ODRecord 的任何引用,ARC 将保持引用的 ODRecord 存活 至少与 因为 _myRecord 存在并且引用未被不同的引用或 nil 覆盖。它是 "at least as long" 因为相同的引用可以存储在其他地方,这些也会影响生命周期。

快到了!要通过参数 return 对 ODRecord 的引用,参数的类型必须是“指向对 ODRecord 的强引用类型变量的指针,即:

- (NSString *)getADrecord:(ODAttributeType)attr
               fromRecord:(ODRecord * __strong *)record

现在的作业如下:

*record = item;

将导致对指向变量的赋值,并且由于该变量是 ODRecord __strong * 类型,ARC 将确保引用的 ODRecord 至少与对它的引用一样长存储在指向的变量中。

您对该方法的调用必须传递一个指向您的变量的指针:

[self getADrecord:attr toRecord:&_myRecord];

备注:

  • "out" 参数在 Objective-C 中不常使用,错误 returns 是一个明显的例外——这些参数属于 NSError * _autoreleasing * 和 Apple将此用法命名为 "call-by-writeback".

  • 有关 ARC 和 return 通过参数计算值的更深入解释,请参阅 Handling Pointer-to-Pointer Ownership issues in ARC and NSError and __autoreleasing

Important:

As pointed out by @matt in the comments your code contains retain and autorelease calls which are forbidden in ARC and therefore if your code is compiling you DO NOT have ARC enabled. For new projects ARC will be enabled, for existing projects you may need to enable it your project's Build Settings, the setting is called "Objective-C Automatic Reference Counting".