【问题标题】:Using self and self properties inside a block in non-arc environment在非弧环境中的块内使用 self 和 self 属性
【发布时间】:2014-11-03 11:00:23
【问题描述】:

我一直在使用块,熟悉在块中使用 self 时的内存管理 非 ARC 环境

但我有两个具体问题:

1) 我知道我可以使用 __block 来避免保留循环,保留块反过来使用 self 可以创建,如下所示:

__block MyClass *blockSelf = self; 
self.myBlock = ^{  
     blockSelf.someProperty = abc;  
     [blockSelf someMethod]; 
};

这肯定会避免保留周期,但通过这样做,我创建了一个自我释放的范围,并最终被其他人释放。因此,当这种情况发生时,self 消失了,blockSelf 指向了一个垃圾值。在自身被释放后执行块时可能会出现一些情况,然后块将在尝试使用已释放的实例时崩溃。我们怎样才能避免这种情况呢?如何检查blockSelf在block执行时是否有效或在self被释放时停止block执行。

2) 在类似的行上,假设我使用如下块:

__block MyClass *blockSelf = self; 
self.myBlock = ^{  
         [blockSelf someMethod:blockSelf.someProperty]; 
    };

// I am taking someProperty in an method argument
-(void) someMethod:(datatype*)myClassProperty
{
  myClassProperty = abc;
}

现在可能会出现 self 没有释放但 someProperty 在 someMethod 的执行开始之前释放的情况(当有多个线程时可能会发生这种情况)。即使我做 self.someProperty = nil;当它被释放时,myClassProperty 不是 nil 并且指向一些垃圾,因此当 someMethod 被执行时,第一行将导致崩溃。我该如何避免这种情况?

【问题讨论】:

  • 1) 不应该是__weak,而不是__block
  • 看看dhoerl.wordpress.com/2013/04/23/…(你需要声明它__weak而不是__block)
  • 我是非arc环境,所以不能使用__weak。
  • 如果self 被释放,谁会调用myBlock

标签: ios objective-c iphone objective-c-blocks self


【解决方案1】:
  1. 这与非 ARC 代码中其他地方的非归零弱引用相同,例如委托等(MRC 没有归零弱引用;因此​​这些是 MRC 中唯一的一种弱引用。但在 ARC 之前的时代,人们仍然能够编写安全代码。)

    基本上,解决方案是您需要一份清晰的所有权地图。 self 负责保持区块存活;或其他一些对象负责使块保持活动状态。

    例如,对于委托,通常,“父”对象是“子”对象的委托;在这种情况下,“父”对象负责保持“子”对象处于活动状态,因此“父”对象的寿命将超过“子”对象,因此反向引用可以是弱且安全的(因为子对象的方法可以只有在父对象存活时才可能被父对象调用)。

    另一方面,如果你有一个异步操作,并且块作为回调被提供给操作,那么通常操作负责持有块。在这种情况下,self 不会保留该块。并且该块将持有对self 的强引用,因此当操作完成时,它仍然可以安全地执行它需要在self 上执行的任何操作。事实上,无论self 是什么对象,它甚至不需要被任何使用它的人保留,因为它是由异步操作间接保留的——它可以只是一个创建、触发和忘记之类的东西。

    如果你有一些东西,块有时由self 保持活动状态,有时由其他东西保持活动状态,那么你应该重新考虑你的设计。您说“释放自我后执行块时可能存在条件”;好吧,你应该描述你的整个设计以及这是如何发生的。因为通常,对于异步执行的东西,self 不需要保持块。

  2. 您的代码确实令人困惑,没有意义。例如,为什么要分配给参数 (myClassProperty)?当参数无论如何都要被覆盖时,传递参数有什么意义?为什么要将局部变量命名为“myClassProperty”?

    我认为您要问的是访问可以在不同线程上更改的属性,以及如何处理该属性的内存管理。 (此问题与块或 ARC/MRC 无关。在 ARC 中同样是一个问题。)

    答案是您需要一个原子属性。您可以创建一个同步属性atomic,或者如果您知道如何手动实现一个原子属性。对象指针类型的原子属性需要做的是,它的 getter 不仅需要返回底层变量,还需要保留和自动释放它,并返回结果。 getter 中的检索和保留发生在临界区,该临界区与 setter 中的临界区同步,包括旧值的释放和新值的保留。基本上,这种同步保证在检索值和将其保留在 getter 之间,值不会在 setter 中释放。并且从 getter 返回的值被保留并自动释放,因此在作用域期间保证它是活动的。

【讨论】:

  • 你没有明白这一点。他不需要减速并将应用程序与同步属性同步。可能是当方法someMethod 执行时,他不再需要someProperty 中的值,但变量myClassProperty 应该具有现有值或nil,他在MRC 上的代码在多线程环境中无法保证。而且我的解决方案更安全,因为它与在 ARC 中执行的代码相同:变量p 在任何情况下都将保留值或为零。
  • @Cy-4AH:不,如果他的代码不安全,你的代码就不安全。 (如果属性是非原子的,则两者都是不安全的;如果是原子的,则两者都是安全的。)
  • 我看到你是从 Java 来到 Objective-C 的。可能是这段代码对 java 不安全,因为从 null 调用方法会抛出异常。但在 Objective-C 中,向 nil 发送消息是安全的。这是常见的做法。
  • @Cy-4AH:别再侮辱别人了。我没有“从 Java 来到 Objective-C”。我是Objective-C的专家。我从来没有说过向 nil 发送消息是不安全的。向 nil 发送消息什么都不做。向解除分配的实例发送消息是不安全的。
  • @Cy-4AH:基本上,你一直在诉诸人身攻击,但没有像受过教育的人那样指出你的答案和你所有的 cmets 根本上是错误的。 “变量 p 将具有保留值或在任何情况下为零”是错误的。如果someProperty 是一个非原子属性,它可以指向一个解除分配的实例,就像在OP 的代码中它可以指向一个解除分配的实例,如果它是一个非原子属性。
【解决方案2】:

2) 的解决方案

__block MyClass *blockSelf = self; 
self.myBlock = ^{
         datatype* p = [blockSelf.someProperty retain];
         [blockSelf someMethod:p]; 
         [p release];
    };

// I am taking someProperty in an method argument
-(void) someMethod:(datatype*)myClassProperty
{
  myClassProperty = abc;
}

对于 1) 不知道如何使用 myBlock。如果它只被self 使用,那么一切都会好起来的。如果它被其他对象使用,那么它也应该在self 保留引用,然后一切都会好起来的。

【讨论】:

  • 这很明显,也可能是正确的,但是如果我的方法也使用其他属性怎么办,比如说它使用 5 个属性,然后保留每个属性然后释放没有意义,对吧?
  • @tarun_sharma,为什么没有意义?弧做同样的事情。它为您添加了retain-release 代码。你有五个参数的方法吗?你不觉得丑吗?在这种情况下,您应该直接使用属性而不将它们作为参数传递给someMethod
  • 您的 (2) 解决方案并不比 OP 的代码更安全。如果 OP 担心在访问属性和将其传递给方法之间释放的值,那么在您的代码中,您同样会遇到在访问属性和保留它之间释放值的问题。两者的解决方案都是原子属性。
猜你喜欢
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-28
  • 1970-01-01
  • 2020-02-03
相关资源
最近更新 更多