使用 objc_getAssociatedObject() 时出现奇怪的 Swift 行为

Posted

技术标签:

【中文标题】使用 objc_getAssociatedObject() 时出现奇怪的 Swift 行为【英文标题】:Strange Swift behaviour when using objc_getAssociatedObject() 【发布时间】:2015-11-19 01:27:04 【问题描述】:

可以通过将此代码放置在操场上来观察行为:

import Foundation
import ObjectiveC

class TestClass 
var obj = TestClass()

let stringValue = "xyz"
let key = "def"

objc_setAssociatedObject(obj, key, stringValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)

//let f = "f"
let returnedString = objc_getAssociatedObject(obj, key)

这有效并从objc_getAssociatedObject 调用返回“xyz”。

但是,如果您从 let f = "f" 行中删除评论。 objc_getAssociatedObject 调用现在返回 nil。

我对设置一个完全不相关的变量如何影响调用感到困惑。

有什么想法吗?

【问题讨论】:

你运行的是哪个 Xcode 版本? 我可以在 7.1.1 的操场上重现这一点,但不能在实际应用程序中重现。也许是游乐场的错误。 我正在考虑这个问题。此外,如果您将定义放在键和运行时调用之间,它会中断。如果将 let f = "f" 向上移动,它就可以正常工作。 我发布的代码是对我在单元测试中的代码的缩减。在测试中,我正在测试一些添加关联属性的代码。所以我想获取我存储的值来检查它是否正确。但是,即使我修改了我正在测试的代码以返回用于存储值的密钥,我也一直得到 nils。我知道这个值被存储了,因为其他代码后来很高兴地使用了这个值。所以我剪切粘贴到一个操场上,看看发生了什么,最后得到了上面的代码。 【参考方案1】:

看起来像一个错误。

objc_... 方法是 Objective-C 运行时的一部分。它们不应该存在于 Swift 中。

这表明他们显然做到了。所以我的猜测是,当您设置启动运行时的方法时会发生一些事情,类似于在 NSUserDefaults 上调用 synchronize

编辑: This NSHipster article 解释说 ObjC 运行时确实在那里。

编辑 2:我尝试了一些实验,我会让你的问题变得更奇怪。将测试用例包装在对象内会产生相同的结果。但是将变量名称更改为下划线可以解决问题:

let _ = "f"

我敢打赌,分配一个变量会覆盖您手动设置的任何运行时关联。下划线只是告诉编译器你没有使用赋值的结果。

【讨论】:

如果将变量名称更改为下划线可以解决问题,这表明操场机制可能会干扰 Objective-C 运行时机制(或编译器将类注册到运行时)。 Xcode 无形地重写您的代码,添加日志功能来驱动结果侧边栏。当您分配给_ 时,该特定代码行不会被重写。我建议提交错误报告(并在此处回复 # 以作记录)。 我不是 OP,但这就是我的暗示,但我仍然不确定该下划线的确切语义是什么。我问过的每个人都说“这意味着你没有使用变量”,但这对 Swift 编译器意味着什么? 见我上面的评论。我最初在单元测试中遇到了这个错误,并在试图弄清楚发生了什么时将其移到了操场上。我不知道它是否存在于应用程序中,因为我的原始代码实际上并未尝试检索该值。只有单元测试可以。

以上是关于使用 objc_getAssociatedObject() 时出现奇怪的 Swift 行为的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)