简体   繁体   English

Objective-c指针

[英]Objective-c pointer

I have a property : 我有一个属性:

@property(nonatomic, assign)UIView *currentView;

when I process the follow code, why it will break? 当我处理以下代码时,为什么会破坏?

_currentView  =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView);     ///break here. 
NSLog(@"v1 %@", v1);

I think the _currentView and v1 both point to a same memory. 我认为_currentViewv1都指向相同的内存。 When use v1 to realese the object, and use _currentView to print the object, it will crash. 当使用v1重新定位对象,并使用_currentView打印对象时,它将崩溃。 I can understand this. 我能理解这一点。

But if the add follow line after v1 release and before print _currentView . 但是如果在v1发布之后和print _currentView之前添加跟随行。 I can`t understand the log. 我无法理解日志。

v1 = nil;

the code like follow 代码如下

_currentView  =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
v1 = nil;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);

print result is : 打印结果是:

> 2012-05-30 15:16:57.314 All[3068:15203] _currentView <UIView:
0x81ccbc0; frame = (0 0; 0 0); layer = <CALayer: 0xa07e5a0>>
> 2012-05-30 15:16:57.798 All[3068:15203] v1 <UIView: 0x81ccbc0; frame =
(0 0; 0 0); layer = <CALayer: 0xa07e5a0>
> 2012-05-30 15:16:59.189 All[3068:15203] _currentView <UIView: 0x81ccbc0; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)
> 2012-05-30 15:17:09.042 All[3068:15203] v1 (null)

Why after invoke v1 release, and log _currentView , it will print 为什么在调用v1发布和log _currentView后会打印出来

_currentView &lt;UIView: 0x81ccbc0; frame = (0 0; 0 0);
 transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)&gt;

This is not necessarily related to the @property attribute (assign or retain) because you are not using accessors 这不一定与@property属性(赋值或保留)有关,因为您没有使用访问器

This is what happens in your code: 这是您的代码中发生的事情:

@property(nonatomic, assign)UIView *currentView;

You declare an ivar to be assign although that is irrelevant in this case since you are not using self.currentView or [self setCurrentView:...]; 声明要伊娃assign虽然,自从你不使用在这种情况下不相关的self.currentView[self setCurrentView:...]; .

_currentView = nil;
// You just made your pointer _currentView point to nil or 0

UIView *v1 = [[UIView alloc] initWithFrame:CGRectZero];
// You created an UIView object and made v1 to point to it. (v1 is NOT the real object)

_currentView = v1;
// You just made _currentView to point the same object v1 points to

NSLog(@"_currentView %@", _currentView);
// and because of that you correctly see the object here (because _currentView points to the view object)

NSLog(@"v1 %@", v1);
// also here (because v1 points to the object from the start)

[v1 release];
// now you release the object pointed by v1 , since no other is retaining it, it gets deallocated BUT note that v1 is still pointing to it, which now is garbage memory!)

//NSLog(@"_currentView %@ v1 %@", _currentView, v1);
// If above line were executed the app will crash because of v1 and _currentView both are pointing to the object that was just released and it is not valid anylonger.

v1 = nil;
// Now you made v1 to point to nothing so next time you use it terrible things will not happen (★) :)

NSLog(@"_currentView %@", _currentView);
// Oh no! you called _currentView and since it was still pointing to the object you released a bit ago the app crashes :(

NSLog(@"v1 %@", v1);
// This is fine, you set v1 to point to nil so it is not pointing to some garbage memory you simply get nil.

(★) Because in objective-c sending methods to nil is harmless, using nil as parameters of other methods is another story (★)因为在objective-c发送方法中nil是无害的,使用nil作为其他方法的参数是另一个故事

Another thing: 另一件事:

Even if you write self.currentView = v1; 即使你写self.currentView = v1; instead of _currentView = v1; 而不是_currentView = v1; results would be the same since the properly is declared as assign. 结果将是相同的,因为正确声明为assign。

Things would be different if you declare the property as retain . 如果将属性声明为retain情况会有所不同。 In that case after you do [v1 release]; 在那种情况下你做[v1 release]; the object will not be deallocated since the object was retained by currentView ( self.currentView = v1 ). 由于currentView( self.currentView = v1 )保留了对象,因此不会释放该对象。 Then if you do v1 = nil v1 will be pointing to nil and the object will be reachable only by currentView. 然后,如果你做v1 = nil v1将指向nil,只有currentView才能访问该对象。 Then if you do _currentView = nil then _currentView will be pointing to nil but the object itself will not be released since you didn't use the accessory method (nor explicitly released) hence you will get a dangling pointer. 然后,如果你执行_currentView = nil那么_currentView将指向nil,但是由于你没有使用附件方法(也没有明确释放),因此对象本身不会被释放,因此你会得到一个悬空指针。

Not all the times properties declared as retain are the solution, it is case by case. 并非所有声明为retain的属性都是解决方案,具体情况也是如此。 I recommend to read a bit more about memory management in Obj-c (at least this ) also a bit about C pointers and then about ARC 我建议在Obj-c中阅读更多关于内存管理的内容(至少这一点 ),还有一些关于C指针然后关于ARC的内容

The reason why you get different printouts for second output is following: 您为第二个输出获得不同打印输出的原因如下:

After you have executed: [v1 release]; 执行完毕后: [v1 release]; both v1 and _currentView are pointing to old block of memory. v1_currentView都指向旧的内存块。 However setting v1 = nil; 但是设置v1 = nil; will set only v1 to nill and not _currentView (remember these are pointers). 将只设置v1到nill而不是_currentView (记住这些是指针)。

I hope this clarifies the things for you. 我希望这能为你澄清一些事情。

Kind regards, 亲切的问候,
Bo

The problem is how you declared the property: 问题是你如何声明财产:

@property(nonatomic, assign)UIView *currentView;

It should be: 它应该是:

@property(nonatomic, retain)UIView *currentView;

When you try to NSLog , it will have a garbage valor, since you release it previously: 当您尝试NSLog ,它将具有垃圾值,因为您先前已将其释放:

[v1 release];
NSLog(@"_currentView %@", _currentView);

Remember that, at this point, when you try to NSLog it, v1 and _currentView will be pointing to the same block of memory. 请记住,此时,当您尝试NSLog时, v1_currentView将指向同一块内存。

Just want to add one point to nacho4d's answer. 只想为nacho4d的答案添加一点。 If you NSLog a deallocated object, sometimes it will crash and sometimes it won't. 如果你NSLog一个解除分配的对象,有时它会崩溃,有时它不会崩溃。 When the object is deallocated, all that happens is it gets added back on to the list of free memory blocks. 当对象被释放时,所有发生的事情都会被重新添加到空闲内存块列表中。 The actual content of the memory still looks like an object and until some or all of the block gets reused, sending messages to it can often work. 内存的实际内容仍然看起来像一个对象,直到部分或全部块被重用,向它发送消息通常可以工作。

One of three things can happen when you NSLog a deallocated object. 当您对已释放的对象进行NSLog时,可能会发生以下三种情况之一。

  • It can log as if it were still alive 它可以记录,好像它还活着
  • A completely different object might respond if it starts exactly at the same place 如果它完全在同一个地方开始,则完全不同的对象可能会响应
  • You get EXC_BAD_ACCESS 你得到EXC_BAD_ACCESS

Whichever one happens is largely a matter of chance. 无论哪一个发生在很大程度上都是机会问题。

You are declaring a property, but you are not using it, you are using the instance variable directly. 您正在声明属性,但您没有使用它,而是直接使用实例变量。 Also you are failing to retain the memory that your variable is pointing to. 此外,您无法保留变量指向的内存。

It looks like you have an instance variable declared in your class: 看起来您在类中声明了一个实例变量:

@interface MyClass : NSObject {
    UIView * _currentView;
}

@end

What you are doing is that you are accessing this directly, without using the property. 你正在做的是你直接访问它,而不使用该属性。 What happens is that you are not retaining the memory when you assign it, which means you are releasing it completely and it gets deleted. 当您分配内存时,您没有保留内存,这意味着您将完全释放内存并将其删除。 To make it work this way, you could do this: 为了使它以这种方式工作,你可以这样做:

UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
[_currentView release];
_currentView = [v1 retain]; // <-- OBSERVE the retain
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView);     // Should not break anymore
NSLog(@"v1 %@", v1);

Later you need to release the object retained in _currentView: 稍后您需要释放_currentView中保留的对象:

-(void)dealloc {
    [_currentView release];   
}

(Observe that you also need to do this if you want to assign a new value to _currentView) (如果要为_currentView分配新值,请注意您还需要执行此操作)

Another way would be to actually use the property that you declared, but instead use a retain property: 另一种方法是实际使用您声明的属性,而是使用retain属性:

@property(nonatomic, retain)UIView *currentView;

To make it accessible, you need to synthesize it in your class implementation: 要使其可访问,您需要在类实现中进行综合:

@implementation MyClass 

@synthesize currentView = _currentView;

/*...*/

@end

That will make the retain be handled for you. 这将使您的保留得到处理。 Also you do not need to think about releasing the previous value stored in the variable, since that will be handled for you. 此外,您不需要考虑释放存储在变量中的先前值,因为这将为您处理。 However you need to access the property this way: 但是,您需要以这种方式访问​​该属性:

   self.currentView

Your code example would look like this: 您的代码示例如下所示:

UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
self.currentView = v1;
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView);     ///Should not break anymore 
NSLog(@"v1 %@", v1);

As you would see from the printout, the two variables still point to the same memory. 正如您在打印输出中看到的那样,这两个变量仍然指向相同的内存。 The different is that since you are retaining the memory, a retain counter is added to it. 不同的是,由于您保留了内存,因此会添加一个保留计数器。 The memory will not be freed until the retain counter has reached 0, which means you need to release it one time for every time you retain it. 在保留计数器达到0之前,内存不会被释放,这意味着每次保留时都需要释放一次。 Also in the later case you need to release the retain in your dealloc method: 同样在后一种情况下,您需要在dealloc方法中释放retain:

-(void)dealloc {
    [_currentView release];   
}

As for you last question this row 至于你最后一个问题这一行

v1 = nil;

will only affect the address to which v1 points to. 只会影响v1指向的地址。 It would not affect the variable _currentView nor the memory it is pointing to. 它不会影响变量_currentView,也不会影响它指向的内存。

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

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