【问题标题】:Keeping objectiveC object valid outside the scope of a function在函数范围之外保持objectiveC对象有效
【发布时间】:2020-04-07 16:55:59
【问题描述】:

在设置作为输入指针的变量时,我对 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

为了澄清我的问题,我想知道我上面提到的 2 个选项中的哪一个是正确的,将引用传递给 record 并最终传递给 _myRecord,因此它将存储正确的值即使在queryResults 的临时列表将被清理之后。

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

谢谢!

【问题讨论】:

  • 如果说[[item retain] autorelease]; 编译,你根本没有使用ARC。那么问题是什么?
  • @matt,我想知道简单地执行record = item 是否足以使该对象指向的数据持续超出函数getADrecord 的范围。因为如果在没有智能指针的情况下使用 c/c++,在这种情况下我可能最终会出现悬空指针。我希望我现在澄清了我的观点
  • 我再说一遍。如果 retain 编译,则整个文件的 ARC 都关闭。这意味着您的所有代码都是错误的,除非您始终自己管理内存。
  • @Matt,好的,感谢您澄清这一点。所以我会放弃这个选项,因为我想避免自己处理内存管理。所以这给我留下了record = item 的第 1 个选项,但它会在离开函数时传递对_myRecord 的引用吗?
  • 没关系!如果您正在使用 ARC,甚至不要考虑内存管理的作用。只需编写代码并相信 ARC 会做正确的事。这就是 ARC 的意思:它是自动的。这就是为什么我建议整个问题毫无意义。

标签: objective-c pointers automatic-ref-counting indirection


【解决方案1】:

我想知道简单地做record = item是否足以让这个对象指向的数据持续超出函数getADrecord的范围

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

因此任何将对象引用分配给record 将对getADrecord 范围之外的引用对象的生命周期产生零影响,因为record 是函数的本地对象。

要通过参数返回 T 类型的值,参数的类型必须是“指向 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

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

在 ARC 下,保存对对象的引用的变量同时具有类型和 所有权属性;此属性通知 ARC 如何处理在变量中存储引用。共同的所有权属性是__strong__weak,假定没有明确的属性__strong。所以你的实例变量声明是以下的简写:

ODRecord __strong *_myRecord;

此声明意味着对于存储到 _myRecord 中的任何对 ODRecord 的引用,ARC 将保持引用的 ODRecord 活动至少与 _myRecord 存在一样长,并且引用不存在被不同的参考或nil覆盖。它“至少与”相同的引用可以存储在其他地方一样长,这些也会影响生命周期。

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

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

现在是一个作业,例如:

*record = item;

将导致对指向变量的赋值,并且由于该变量的类型为ODRecord __strong *,ARC 将确保被引用的ODRecord 至少只要对它的引用存储在指向的变量中就会存在变量。

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

[self getADrecord:attr toRecord:&_myRecord];

注意事项:

重要

正如@matt 在 cmets 中所指出的,您的代码包含在 ARC 中被禁止的 retainautorelease 调用,因此如果您的代码正在编译,则您没有启用 ARC。对于新项目将启用 ARC,对于现有项目,您可能需要在项目的 Build Settings 中启用它,该设置称为“Objective-C 自动引用计数”。

【讨论】:

    【解决方案2】:

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

    record = item 显然是不够的,因为当records离开作用域时,record的retain count就消失了,也就是函数返回的时候。

    但是您所做的 - 为 每个 项目调用自动释放可确保 所有 项目保持分配一段时间,而不仅仅是“记录”。

    【讨论】:

    • 谢谢,我已将函数更改为在找到一条记录后返回。这是我的意图——只保留一个唱片外壳。
    • 我只是想通过使用 ARC 确定选项 2 (record = [[item retain] autorelease]) 是否有替代方案...我认为 ARC 会自动激活,除非另有说明(使用 autoreleasepool),不是是这样吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多