ARC 中禁用了转换内存位置...那么如何获取对象的内容?

Casting a memory location is disabled in ARC... How to get contents of the object then?

我曾经在 Objective-c 中可以做到这一点。 现在 ARC 禁用了这个:

NSString *mice = @"dogs eat cats";
long dog = (long)mice; 
NSString *appled = (NSString *)dog;

我是不是漏掉了什么。如果我知道我的对象的地址,我怎样才能得到它的内容?

您需要先将其转换为 void *

NSString *appled = (__bridge NSString *)((void *)dog);

已更新。 因为反对票 post 我需要添加一个警告:Obj-C 足够灵活,可以让您修复此类编译器错误, 但如果你想这样做 - 你需要完全确定你在做什么以及为什么。

以非对象类型存储对象指针将像 __unsafe_unretained 变量一样。

__unsafe_unretained specifies a reference that does not keep the referenced object alive and is not set to nil when there are no strong references to the object. If the object it references is deallocated, the pointer is left dangling. (source)

因此,即使您需要进行此类类型转换(出于任何原因),并且您希望您的对象有效 - 您也需要确保该对象至少具有一个强引用。

如果您不想保留对原始对象的强引用,您可以使用 __bridge_retained__bridge_transfer

自己计算引用
NSString *mice = @"dogs eat cats";
long dog = (long)(__bridge_retained CFTypeRef)mice;
// here original mice object has retain count +1

NSString *appled = (__bridge_transfer NSString *)((void *)dog);
// here __bridge_transfer decreased previously gained retain count by 1

And it uses less memory (in your own comment on your question)

它使用 完全 相同数量的内存:将 n 位指针存储为 n 位整数需要 n 位!

您应该仔细检查您这样做的原因以及您的代码在 ARC 下是否会保持安全。

在 ARC 之前,您可以 "hide" 对对象的引用,并且该对象将一直存在,直到您发出明确的 release()

Post-ARC 有 bridge 强制转换(存在多种类型),使您能够明确地对对象生命周期负责,并在以后将责任转回。

因此,

到 "hide" 您在示例中所做的参考需要 两个 桥梁铸件,每个方向一个。 如果未能正确执行此操作,将导致对象在您尝试使用已恢复的指针时被 ARC 和内存自动释放 faults/corruption。

ARC 文档描述了各种桥接转换以及何时使用它们。但是,考虑到您对内存使用的评论,请非常仔细地考虑您是否应该这样做。

HTH


附录:评论跟进

您误解了内存地址、对象生命周期等在 (Objective-)C 中的工作方式。我会 强烈 建议您在弄清楚这一点之前不要尝试使用任何桥梁模型。举一两个比喻,看看能不能帮助你理解。

Warning: Analogies always break down at some point, you should not push them too far and I'll try not to!

让我们看看你的评论,它开始:

sizeof(long) = 8, sizeof(NSString)?-->"can't apply 'sizeof' to the class NSString".

您是正确的,在 Objective-C 中不允许使用 class 类型的 sizeof,但这不是这里的问题所在。查看您问题中的代码:

NSString *mice = @"dogs eat cats";
long dog = (long)mice;

此处您不是在 NSString 类型的值上操作,而是在 NSString * 类型的值上操作 - 这两个 非常 不同。

与其考虑 NSString Objective-C 中的对象类型,不如想想现实世界中的类比:建筑物。在这个类比中,对象引用的等价物是什么,例如 NSString *?是大楼的地址。

建筑物地址与建筑物是一回事吗?不,它和建筑物一样大吗?不可以。您可以将地址写在一张纸上,然后放在口袋里。你不能把建筑物放在口袋里(除非你是格列佛 ;-))。您甚至可以将写有地址的纸片放在建筑物内,它很容易装进去。

建筑物地址的作用是让您找到建筑物,但不能保证该地址处有建筑物 - 它可能已被拆除,并且可能已被新建筑物取代有不同的目的。

这类似于对象的地址,它使您能够定位对象,但不能保证对象存在 – 它可能已被销毁,并且它的旧位置现在可以成为其他对象的一部分。

您要进行的比较是 sizeof(long)sizeof(NSString *) 的比较。在当前的 64 位 Objective-C 上,您会在 8.

上找到这两个结果

Note: on the (rare) occasions you need to store an address in a integer you should not use the type long, rather you should use the type intptr_t which is a standard C integer type of the same size as an address. So your code should really have been:

NSString *mice = @"dogs eat cats";
intptr_t dog = (intptr_t)mice; 

(That said, you probably shouldn't have written this code at all.)

回到你的评论,你继续:

But in it's place as an example, if I were to create a structure of 4 longs... the sizeof(struct4longs) = 32. Lets say you have a structure that takes 1mb of ram. Under ARC using their rules, to keep the reference, I would allocate 1mb to keep the reference to the 1mb... because the old way of referencing(keeping only addresses) is no longer allowed-->NSString *appled = (NSString *)dog;

不,不,不。地址大小相同,无论 它引用什么。

在我们的建筑物类比中,地址“330 5th Ave, New York”和“350 5th Ave, New York”的大小完全相同。第一个是Panera Bread cafe,第二个是帝国大厦——大小不一!

Converting an object address to an integer does not save any space at all.

pre-ARC和post-ARC的区别

坚持我们的类比:在 ARC 之前的时代建造建筑物(alloc/initnew 等),标记为正在使用(retain),标记为不再需要 (release),并最终拆除(对象销毁)手动

一栋建筑可以空着不用,就这样立在那里,用完了 space,除非建筑商(程序员)出现并拆除它(程序员为每个匹配 release retain).

你可以在你的地址簿中写一个建筑物的地址(它的地址存储在一个指针类型的变量中,比如NSString *),它对建筑物的寿命没有任何影响。

您可以保留一份建筑物地址的模糊副本,比如将其写入代码并将其放入您的日历中(相当于将对象地址放入整数类型变量中),它仍然对建筑物的生命周期。

So in the pre-ARC days your scheme "worked" – in that you could hide and recover object addresses – but it had no real purpose, it didn't save any space, just made your code harder to understand and more error prone. You had to use manual calls (retain, release) to control the lifetime of your objects.

post-ARC 世界改变了这一切。

在post-ARC世界建筑拆除由自动机器人系统接管,不再使用的建筑被拆除。不需要人类(程序员)采取任何行动。

那么机器人系统如何知道何时可以拆除建筑物?

彩纸! (我不是开玩笑,但记住这是一个类比)

规则很简单:在一张黄纸上写下建筑物的地址,机器人拆迁队不会拆除建筑物。

拿一块橡皮擦擦掉每张黄纸上的建筑物名称,机器人工作人员将在他们选择的某个时间搬进来并拆除它。

如果你扔掉或烧掉这张黄纸,也会发生同样的事情。机器人拆除小组只考虑某人拥有的黄纸。 (这包括在建筑物内发现的黄纸 提供 该建筑物的地址写在一张黄纸上,如果那张黄纸在建筑物中,则该建筑物的地址是写在另一张黄纸上……等等,并在某个时候提供一张不在建筑物内的黄纸,这会启动链条。)

将地址写在一张白纸上,机器人就会忽略这张纸。只有拥有的黄纸才能防止建筑物被摧毁。

旧的 ARC 前代码在新的 post-ARC 世界中所做的是将建筑物的地址从黄色纸片转移到白色纸片,然后扔掉黄色纸片.当有急切的机器人拆除人员希望拆除那里的建筑物时,情况就不妙了。

稍后您尝试将白纸上的地址复制回黄色纸上,希望机器人还没有找到建筑物...希望破灭,这就是生活。把重要的东西留在大楼里了?一件无价的艺术品可能挂在墙上?艰难。

类比够了,回到Objective-C:

黄纸在技术上称为强引用,post-ARC世界中对象类型(例如NSString *)的变量是(通常,此时可以忽略少数异常)隐式标记为强(例如 __strong NSString *)。

白纸都是非对象指针类型变量(如longintptr_t、甚至int *等)和对象指针类型变量显式标记为__unsafe_unretained – 这个名字应该告诉你一切,只在任何这样的变量中存储地址是 不安全的 因为对象不会被 保留 , 自动对象回收将销毁它。

结论:

不要做你在 ARC 之前的日子里所做的事情。

  • 在那些日子里,它不保存任何内存,也没有任何用处,但它并非不安全。

  • 在 post-ARC 时代它不仅没有用处,而且 不安全。

只需将您的地址存储为地址。

您可能想知道为什么存在桥接转换。好吧,在特殊情况下需要它们,它们不适合一般用途。当您接触到这些情况时,如果您接触到这些情况,您将了解它们以及如何安全地使用它们。

希望以上内容能帮助您解决这个问题!