【问题标题】:Does referencing an iOS app delegate in a block create a copy of the delegate object?在块中引用 iOS 应用程序委托会创建委托对象的副本吗?
【发布时间】:2014-04-03 01:40:31
【问题描述】:

考虑以下示例:

- (void)exampleFunction
{
  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  SomeClass *classObject = [[SomeClass alloc] init];
  [classObject someFunctionThatDoesBackgroundWorkAndCallsACompletionBlock:^(NSDictionary *results) {
    SomeNeatObject *object = appDelegate.someNeatObject;
    [object doSomeNeatStuff:results];
  }];
}

假设完成块在主/UI线程上执行以避免额外的疯狂,这是我不确定的:

  1. 在块内引用变量时,appDelegate 变量是否按照正常的块规则复制到堆栈中?
  2. 如果#1 是,这是一种好的/安全的做法吗?如果您在随后被分派到 GCD 队列的块中执行此操作,我可能会看到这可能会导致问题,因为 .someNeatObject 可能已更改状态/变为 nil?

我意识到 appDelegate 不应该被过度使用,但是如果你使用 Apple 的模板来初始化 Core Data 堆栈(在至少在 iOS 项目上)。这种特殊情况(核心数据)让我担心,因为该堆栈的大部分不是线程安全的,复制它也不是一个好主意。

提前致谢。

【问题讨论】:

  • 我什至不会将我的 CoreData 堆栈放入应用程序委托中。
  • “在块中引用 iOS 应用程序委托会创建委托对象的副本吗?”不会。一个块会保留其中使用的根对象,但它不会复制任何内容。任何事物。好吧,从技术上讲,人们可能会争辩说原语被复制了,但肯定没有对象。
  • @Kevin 我不确定(这就是我问这个问题的原因),因为 ARC 固有地复制块,并且根据苹果的文档 (developer.apple.com/library/ios/documentation/cocoa/conceptual/…),块复制引用的任何实例变量。那么对象只是递增而不是复制吗?
  • @WayneHartman 同意,但如果你使用 Apple 的默认模板,那就是卡住了。另外这只是一个例子:)
  • @MattS。我不确定你在哪里看到了关于在那里复制对象的任何信息。也许在__block 存储类型下?这会复制变量,即指针,而不是对象本身。

标签: ios objective-c macos


【解决方案1】:

您的示例将无法编译,因为未定义委托。我假设您的意思是“SomeNeatObject *object = appDelegate.someNeatObject;”

在本例中,appDelegate 是一个变量,其值将被块捕获。它与任何其他变量没有什么不同。

这不是不安全的,因为 [[UIApplication sharedApplication] delegate] 总是返回相同的东西,除非你改变了委托,你可能没有。

这里的基本概念是:appDelegate 是一个变量,它指向(或引用)一个对象 AppDelegate 类型的(或实例)。在 iOS 应用程序中有一个这样的实例,由 [[UIApplication sharedApplication] delegate] 返回。如果您在块内创建对 appDelegate 的引用,则您正在制作变量的副本,而不是对象。所以在你的代码块中:

SomeNeatObject *object = appDelegate.someNeatObject;

这在语义上与将以下代码放入块中相同(省略强制转换):

SomeNeatObject *object = [[UIApplication sharedApplication] delegate].someNeatObject;

所引用的 SomeNeatObjects 是同一个东西。

稍微高级一点的概念:任何对象在内存中都有一个地址(一个整数,通常以十六进制形式出现)。如果两个变量具有相同的十六进制值,则它们指向同一个对象。如果它们具有不同的值,则它们指向不同的对象。在您的示例中,appDelegate(块外)和 appDelegate(块内)具有相同的值,因此指向同一个对象。

如果你这样做:

AppDelegate * otherDelegate = [appDelegate copy];

然后,您将制作 appDelegate 指向的 object 的副本。但请不要这样做。

【讨论】:

  • 很好地解决了 appDelegate / delegate 问题。编辑了我的代码。
  • 我不确定我是否一定同意你的说法——如果你复制委托——它会复制挂在上面的变量的状态,所以这不意味着变量不一定匹配当前版本?
  • 您在哪里复制了委托?我没有看到对复制方法的调用。
  • 除非你把 __block 放在指针赋值行中,否则它本质上是复制的,这是我的理解吗?
  • 没有。 __block 表示可以在块中修改变量,并且更改将在块外可见。
【解决方案2】:

它的保留计数确实增加了。补救方法是使用__weak 属性对其进行标记:

- (void)exampleFunction {
  __weak AppDelegate *weakDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

  SomeClass *classObject = [[SomeClass alloc] init];
  [classObject someFunctionThatDoesBackgroundWorkAndCallsACompletionBlock:^(NSDictionary *results) {
    SomeNeatObject *object = weakDelegate.someNeatObject;
    [object doSomeNeatStuff:results];
  }];
}

【讨论】:

  • 呃,我担心会发生这种情况。我很想知道其他人会怎么说,但我很确定这就是问题所在。
  • 保留计数确实会增加,但这很可能是无害的。
  • 你能不能也把 __block 扔到 *appDelegate 上来避免复制状态?
  • @SofiSoftwareLLC 尽管应用程序委托的生命周期是应用程序的整个生命周期,但为什么不一直使用良好的编码实践呢?我认为他不应该滥用应用委托作为存储全局状态的地方。
  • @MattS。 __block 仅在您不使用 ARC 时防止增加保留计数。
猜你喜欢
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
相关资源
最近更新 更多