繁体   English   中英

在 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;

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

它使用更少的内存(在你对你的问题的评论中)

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

您应该仔细检查为什么要这样做以及您的代码在 ARC 下是否仍然安全。

在 ARC 之前,您可以“隐藏”对对象的引用,并且该对象将一直存在,直到您发出明确的release()为止。

在 ARC 之后有转换(存在多种类型),它使您能够明确地承担对象生命周期的责任,并在以后将责任转移回来。

因此,要像您在示例中所做的那样“隐藏”参考,需要两个桥形模型,每个方向一个。 当您尝试使用恢复的指针时,如果不正确执行此操作将导致对象被 ARC 自动释放和内存故障/损坏。

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

HTH


附录:评论跟进

您误解了内存地址、对象生存期等在 (Objective-)C 中的工作方式。 强烈建议您在弄清楚这一点之前不要尝试使用任何桥式转换。 让我们看看我是否可以帮助您理解,借助一两个类比。

警告:类比总会在某些时候崩溃,你不应该把它们推得太远,我会尽量不要!

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

sizeof(long) = 8, sizeof(NSString)?-->“不能将‘sizeof’应用于类 NSString”。

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

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

在这里,您不是对NSString类型的值进行操作,而是对NSString *类型之一进行操作——这两者非常不同。

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

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

建筑物地址的作用是使您能够定位建筑物,但它并不能保证该地址上有建筑物 - 它可能已被拆除,并且可能已被具有不同用途的新建筑物所取代。

这类似于一个对象的地址,它使您能够定位该对象,但不能保证该对象存在——它可能已被销毁,它的旧位置现在可能是某个其他对象的一部分。

您所进行的比较是sizeof(long)sizeof(NSString *)相比。 在当前的 64 位 Objective-C 上,您会在8上找到这两个结果。

注意:在(极少数)情况下,您需要将地址存储在整数中,您不应使用long类型,而应使用intptr_t类型,它是与地址大小相同的标准 C 整数类型。 所以你的代码应该是:

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

(也就是说,您可能根本不应该编写此代码。)

回到你的评论,你继续:

但举个例子,如果我要创建一个 4 长的结构...... sizeof(struct4longs) = 32。假设你有一个需要 1mb ram 的结构。 在 ARC 使用他们的规则下,为了保留引用,我会分配 1mb 来保留对 1mb 的引用......因为不再允许使用旧的引用方式(仅保留地址)--> NSString *appled = (NSString * )狗;

不,不,不。 无论地址引用什么,地址的大小都是相同的。

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

将对象地址转换为整数根本不节省任何空间。

ARC 前和 ARC 后的区别

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

建筑物可以空置和未使用,它只是站在那里,占用空间,除非建造者(程序员)出现并拆除它(程序员为每个retain匹配一个release )。

您可以在地址簿中写入建筑物的地址(将其地址存储在指针类型的变量中,例如NSString * ),它对建筑物的生命周期没有任何影响。

您可以保留建筑物地址的隐蔽副本,例如将其写入代码并将其放入日历中(相当于将对象地址放入整数类型变量中),它仍然对建筑物的生命周期没有影响。

因此,在 ARC 之前的时代,您的方案“有效”——您可以隐藏和恢复对象地址——但它没有真正的目的,它没有节省任何空间,只是让您的代码更难理解,更容易出错。 您必须使用手动调用( retainrelease )来控制对象的生命周期。

后 ARC 世界改变了这一切。

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

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

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

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

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

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

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

在新的后 ARC 世界中,旧的前 ARC 代码所做的是将建筑物的地址从一张黄色的纸转移到一张白色的纸上,然后将黄色的纸扔掉。 当有一个渴望拆除建筑物的机器人拆除人员时,这并不好。

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

打个比方,回到Objective-C:

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

白纸都是非对象指针类型变量(例如longintptr_t ,甚至int *等)和对象指针类型变量明确标记为__unsafe_unretained – 该名称应该告诉您一切,仅在任何此类中存储地址变量是不安全的,因为对象不会被保留,自动对象回收会破坏它。

结论:

不要做你在前 ARC 时代正在做的事情。

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

  • 在后ARC天它不仅没有实用的目的,它不安全的。

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

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

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

您需要先将其void *void *

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

更新。 由于对这篇文章的反对票我需要添加一个警告:Obj-C 足够灵活,可以让您修复此类编译器错误,但是如果您想这样做 - 您需要完全确定自己在做什么以及为什么.

以非对象类型存储对象指针的作用类似于__unsafe_unretained变量。

__unsafe_unretained指定一个引用,该引用不会使被引用的对象保持活动状态,并且在没有对该对象的强引用时不设置为 nil。 如果它引用的对象被释放,则指针悬空。 (来源)

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

如果您不想保留对原始对象的强引用,您可以使用__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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM