简体   繁体   English

在 ARC 中禁止投射内存位置……那么如何获取对象的内容?

[英]Casting a memory location is disabled in ARC… How to get contents of the object then?

I used to be able to do this in Objective-c.我曾经能够在 Objective-c 中做到这一点。 Now ARC disables this:现在ARC禁用了这个:

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

Am I missing something.我是不是错过了什么。 If I know the address of my object how can I get the contents of it?如果我知道我的对象的地址,我怎样才能得到它的内容?

And it uses less memory (in your own comment on your question)它使用更少的内存(在你对你的问题的评论中)

It uses exactly the same amount of memory: storing an n-bit pointer as an n-bit integer takes n-bits!它使用完全相同的内存量:将 n 位指针存储为 n 位整数需要 n 位!

You should carefully review why you are doing this and whether your code will remain safe under ARC.您应该仔细检查为什么要这样做以及您的代码在 ARC 下是否仍然安全。

Pre-ARC you could "hide" a reference to an object and that object would stay around until you issued an explicit release() .在 ARC 之前,您可以“隐藏”对对象的引用,并且该对象将一直存在,直到您发出明确的release()为止。

Post-ARC there are bridge casts (multiple kinds exist) which enable you to explicitly take responsibility for object lifetime, and to later transfer the responsibility back.在 ARC 之后有转换(存在多种类型),它使您能够明确地承担对象生命周期的责任,并在以后将责任转移回来。

To "hide" the reference as you do in your example therefore requires two bridge casts, one in each direction.因此,要像您在示例中所做的那样“隐藏”参考,需要两个桥形模型,每个方向一个。 Failure to do this correctly will result in objects being automatically released by ARC and memory faults/corruption when you attempt to use your recovered pointers.当您尝试使用恢复的指针时,如果不正确执行此操作将导致对象被 ARC 自动释放和内存故障/损坏。

The ARC documentation describes the various bridge casts and when to use them. ARC 文档描述了各种桥型转换以及何时使用它们。 However given your comment on memory use consider very carefully whether you should be doing this.但是,鉴于您对内存使用的评论,请仔细考虑您是否应该这样做。

HTH HTH


Addendum : Comment followup附录:评论跟进

You are misunderstanding how memory addresses, object lifetime, etc. work in (Objective-)C.您误解了内存地址、对象生存期等在 (Objective-)C 中的工作方式。 I would strongly recommend you do not attempt to use any bridge casts until you have figured this out.强烈建议您在弄清楚这一点之前不要尝试使用任何桥式转换。 Let's see if I can help you understand, with the help of an analogy or two.让我们看看我是否可以帮助您理解,借助一两个类比。

Warning: Analogies always break down at some point, you should not push them too far and I'll try not to!警告:类比总会在某些时候崩溃,你不应该把它们推得太远,我会尽量不要!

Let's look at your comment, it starts:让我们看看你的评论,它开始:

sizeof(long) = 8, sizeof(NSString)?-->"can't apply 'sizeof' to the class NSString". sizeof(long) = 8, sizeof(NSString)?-->“不能将‘sizeof’应用于类 NSString”。

You are correct that in Objective-C taking the sizeof of a class type is disallowed, but that is not what the issue is here.您是正确的,在 Objective-C 中不允许使用类类型的sizeof ,但这不是问题所在。 Look at the code in your question:看看你问题中的代码:

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

Here you are not operating on a value of type NSString but on one of type NSString * - these two are very different.在这里,您不是对NSString类型的值进行操作,而是对NSString *类型之一进行操作——这两者非常不同。

Instead of considering NSString which is an object type in Objective-C think of a real world analogy: a building.与其考虑在 Objective-C 中作为对象类型的NSStringNSString考虑一个真实世界的类比:建筑物。 In this analogy what is the equivalent of an object reference, such as NSString * ?在这个类比中,什么是对象引用的等价物,例如NSString * It is the address of the building.这是大楼的地址。

Is a building address the same kind of thing as a building?建筑物地址和建筑物一样吗? No. Is it the same size as a building?不是。它和建筑物一样大吗? No. You can write the address on a piece of paper and put it in your pocket.不需要。您可以将地址写在一张纸上,然后放在口袋里。 You can't put a building in your pocket (unless you are Gulliver ;-)).你不能把建筑物放在口袋里(除非你是格列佛;-))。 You can even put your piece of paper with an address on it inside a building, it fits easily.您甚至可以将写有地址的纸放在建筑物内,它很容易贴合。

What the address of a building does is enable to you locate the building, but it doesn't guarantee that there is a building at the address - it could have been demolished, and may have been replaced by a new building with a different purpose.建筑物地址的作用是使您能够定位建筑物,但它并不能保证该地址上有建筑物 - 它可能已被拆除,并且可能已被具有不同用途的新建筑物所取代。

This is analogous to the address of an object, it enables you to locate the object, but is does not guarantee the object exists – it could have been destroyed, and its old location could now be part of some other object.这类似于一个对象的地址,它使您能够定位该对象,但不能保证该对象存在——它可能已被销毁,它的旧位置现在可能是某个其他对象的一部分。

The comparison you were after is what is sizeof(long) compared to sizeof(NSString *) .您所进行的比较是sizeof(long)sizeof(NSString *)相比。 On current 64-bit Objective-C you'll find both of these result on 8 .在当前的 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.注意:在(极少数)情况下,您需要将地址存储在整数中,您不应使用long类型,而应使用intptr_t类型,它是与地址大小相同的标准 C 整数类型。 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.) (也就是说,您可能根本不应该编写此代码。)

Back to your comment, you continue:回到你的评论,你继续:

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

No, no, no.不,不,不。 An address is the same size regardless of what it references.无论地址引用什么,地址的大小都是相同的。

In our analogy of buildings the addresses "330 5th Ave, New York" and "350 5th Ave, New York" are exactly the same size.在我们对建筑物的类比中,地址“330 5th Ave, New York”和“350 5th Ave, New York”的大小完全相同。 The first is a Panera Bread cafe, the second is the Empire State Building – the buildings are not the same size!第一个是 Panera Bread 咖啡馆,第二个是帝国大厦——建筑物的大小不一样!

Converting an object address to an integer does not save any space at all.将对象地址转换为整数根本不节省任何空间。

The difference between pre-ARC and post-ARC ARC 前和 ARC 后的区别

Sticking with our analogy: In pre-ARC times buildings were built ( alloc / init , new , etc.), marked as in use ( retain ), marked as no longer required ( release ), and eventually demolished (object destruction) manually .坚持我们的类比:在 ARC 之前的时代,建筑物被建造( alloc / initnew等),标记为使用中( retain ),标记为不再需要( release ),并最终手动拆除(对象销毁)。

A building could be left empty and unused and it just stood there, using up space, unless the builder (programmer) came along and demolished it (the programmer matches a release for every retain ).建筑物可以空置和未使用,它只是站在那里,占用空间,除非建造者(程序员)出现并拆除它(程序员为每个retain匹配一个release )。

You could write the address of a building in your address book (store its address in a pointer-typed variable such as NSString * ), it did not have any effect on the lifetime of the building.您可以在地址簿中写入建筑物的地址(将其地址存储在指针类型的变量中,例如NSString * ),它对建筑物的生命周期没有任何影响。

You could keep an obscured copy of the building address, say write it in code and put it in your calendar (the equivalent of you placing an object address in an integer typed variable), it still had no effect on the lifetime of the building.您可以保留建筑物地址的隐蔽副本,例如将其写入代码并将其放入日历中(相当于将对象地址放入整数类型变量中),它仍然对建筑物的生命周期没有影响。

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.因此,在 ARC 之前的时代,您的方案“有效”——您可以隐藏和恢复对象地址——但它没有真正的目的,它没有节省任何空间,只是让您的代码更难理解,更容易出错。 You had to use manual calls ( retain , release ) to control the lifetime of your objects.您必须使用手动调用( retainrelease )来控制对象的生命周期。

The post-ARC world changes all this.后 ARC 世界改变了这一切。

In the post-ARC world building demolition was taken over by an automatic robotic system, buildings no longer in use are demolished.在后 ARC 世界中,建筑物拆除由自动机器人系统接管,不再使用的建筑物被拆除。 No action required by humans (programmers).人类(程序员)无需采取任何行动。

So how does the robotic system know when a building can be demolished?那么机器人系统如何知道何时可以拆除建筑物呢?

Coloured paper!彩纸! (I do not joke, but remember this is an analogy) (我不是开玩笑,但请记住这是一个类比)

The rule is simple: write down the address of a building on a piece of yellow paper and the robot demolition crew will not demolish the building.规则很简单:在一张黄纸上写下建筑物的地址,机器人拆除人员不会拆除建筑物。

Get an eraser and rub out the buildings name from every piece of yellow paper it is on and the robot crew will, at some time of their choosing , move in and demolish it.拿一块橡皮擦,从它上面的每张黄纸上擦掉建筑物的名称,机器人船员将在他们选择的某个时间搬进来并拆除它。

Same thing happens if you throw away or burn the piece of yellow paper.如果你扔掉或烧掉那张黄纸,也会发生同样的事情。 Only yellow paper owned by someone is considered by the robot demolition crew.机器人拆迁队只考虑某人拥有的黄纸。 (This includes yellow paper found inside buildings provided the address of that building is written down on a piece of yellow paper, and if that piece of yellow paper is in a building then that building's address is written on another piece of yellow paper... etc., and provided at some point there is a piece of yellow paper not in a building which starts the chain off.) (这包括在建筑物内发现的黄纸,前提是该建筑物的地址写在一张黄纸上,如果那张黄纸在建筑物内,则该建筑物的地址写在另一张黄纸上......等等,并且在某些时候提供了一张不在建筑物中的黄色纸,它可以启动链条。)

Write the address on a piece of white paper and the robots just ignore the piece of paper.把地址写在一张白纸上,机器人就会忽略这张纸。 Only owned yellow paper prevents the building being destroyed.只有拥有的黄纸才能防止建筑物被破坏。

What your old pre-ARC code does in the the new post-ARC world is transfer the address of a building from a yellow piece of paper to a white piece, and then throws aways the yellow piece.在新的后 ARC 世界中,旧的前 ARC 代码所做的是将建筑物的地址从一张黄色的纸转移到一张白色的纸上,然后将黄色的纸扔掉。 Not good when there is an eager robot demolition crew looking to demolish buildings out there.当有一个渴望拆除建筑物的机器人拆除人员时,这并不好。

Later you try to copy the address from your white piece of paper back onto a yellow piece in the hope that the robots haven't found the building yet... hopes get dashed, that's life.稍后,您尝试将白纸上的地址复制回黄色纸上,希望机器人还没有找到建筑物……希望破灭了,这就是生活。 Left something important in the building?在大楼里留下了什么重要的东西? A priceless work of art maybe left hanging on the wall?一件无价的艺术品也许会挂在墙上? Tough.艰难的。

Enough analogy, back to Objective-C:打个比方,回到Objective-C:

The yellow pieces of paper are technically called strong references , a variable of object type (eg NSString * ) in the post-ARC world is (usually, the few exceptions can be ignored at this point) implicitly marked as strong (eg __strong NSString * ).黄色的纸片在技术上称为强引用,在后 ARC 世界中对象类型(例如NSString * )的变量(通常,此时可以忽略少数例外)隐式标记为强__strong NSString * (例如__strong NSString * )。

The white pieces of paper are all non-object pointer typed variables (eg long , intptr_t , and even int * etc.) and object pointer typed variables explicitly marked as __unsafe_unretained – that name should tell you everything, storing an address only in any such variable is unsafe as the object will not be retained , the automatic object reclamation will destroy it.白纸都是非对象指针类型变量(例如longintptr_t ,甚至int *等)和对象指针类型变量明确标记为__unsafe_unretained – 该名称应该告诉您一切,仅在任何此类中存储地址变量是不安全的,因为对象不会被保留,自动对象回收会破坏它。

Conclusion:结论:

Do not do what you were doing in the pre-ARC days.不要做你在前 ARC 时代正在做的事情。

  • In those days it saved no memory and had no useful purpose, however it wasn't unsafe.在那些日子里,它不保存任何内存,也没有任何有用的用途,但它并不是不安全的。

  • In the post-ARC days it not only has no useful purpose, it is unsafe.在后ARC天它不仅没有实用的目的,它不安全的。

Just store your addresses as addresses.只需将您的地址存储为地址。

You might wonder why bridging casts exist.您可能想知道为什么存在桥接类型转换。 Well there are special cases where they are needed, they are not for general use.好吧,有一些特殊情况需要它们,它们不用于一般用途。 When, and if, you get to those cases you'll read about them and how to use them safely.当您遇到这些案例时,您将了解它们以及如何安全地使用它们。

I hope the above helps you sort this out!希望以上内容能帮助你解决这个问题!

You need to cast it to void * first:您需要先将其void *void *

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

Updated.更新。 Because of down votes on this post I need to add a warning: Obj-C is flexible enough to let you fix such kind of compiler errors, but if you tying to do so - you need to be completely sure what you are doing and why.由于对这篇文章的反对票我需要添加一个警告:Obj-C 足够灵活,可以让您修复此类编译器错误,但是如果您想这样做 - 您需要完全确定自己在做什么以及为什么.

Storing object pointer in non-object type will act like __unsafe_unretained variable.以非对象类型存储对象指针的作用类似于__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. __unsafe_unretained指定一个引用,该引用不会使被引用的对象保持活动状态,并且在没有对该对象的强引用时不设置为 nil。 If the object it references is deallocated, the pointer is left dangling.如果它引用的对象被释放,则指针悬空。 (source) (来源)

So even if you need to make such type casts (for any reason), and you want your object to be valid - you need to ensure that this object has at least one strong reference.因此,即使您需要进行此类类型转换(出于任何原因),并且您希望您的对象有效 - 您也需要确保该对象至少有一个强引用。

And in case if you don't want to keep strong references to original object, you can count references by yourself using __bridge_retained and __bridge_transfer如果您不想保留对原始对象的强引用,您可以使用__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