繁体   English   中英

Objective-c指针

[英]Objective-c pointer

我有一个属性:

@property(nonatomic, assign)UIView *currentView;

当我处理以下代码时,为什么会破坏?

_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);

我认为_currentViewv1都指向相同的内存。 当使用v1重新定位对象,并使用_currentView打印对象时,它将崩溃。 我能理解这一点。

但是如果在v1发布之后和print _currentView之前添加跟随行。 我无法理解日志。

v1 = nil;

代码如下

_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);

打印结果是:

> 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)

为什么在调用v1发布和log _currentView后会打印出来

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

这不一定与@property属性(赋值或保留)有关,因为您没有使用访问器

这是您的代码中发生的事情:

@property(nonatomic, assign)UIView *currentView;

声明要伊娃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.

(★)因为在objective-c发送方法中nil是无害的,使用nil作为其他方法的参数是另一个故事

另一件事:

即使你写self.currentView = v1; 而不是_currentView = v1; 结果将是相同的,因为正确声明为assign。

如果将属性声明为retain情况会有所不同。 在那种情况下你做[v1 release]; 由于currentView( self.currentView = v1 )保留了对象,因此不会释放该对象。 然后,如果你做v1 = nil v1将指向nil,只有currentView才能访问该对象。 然后,如果你执行_currentView = nil那么_currentView将指向nil,但是由于你没有使用附件方法(也没有明确释放),因此对象本身不会被释放,因此你会得到一个悬空指针。

并非所有声明为retain的属性都是解决方案,具体情况也是如此。 我建议在Obj-c中阅读更多关于内存管理的内容(至少这一点 ),还有一些关于C指针然后关于ARC的内容

您为第二个输出获得不同打印输出的原因如下:

执行完毕后: [v1 release]; v1_currentView都指向旧的内存块。 但是设置v1 = nil; 将只设置v1到nill而不是_currentView (记住这些是指针)。

我希望这能为你澄清一些事情。

亲切的问候,

问题是你如何声明财产:

@property(nonatomic, assign)UIView *currentView;

它应该是:

@property(nonatomic, retain)UIView *currentView;

当您尝试NSLog ,它将具有垃圾值,因为您先前已将其释放:

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

请记住,此时,当您尝试NSLog时, v1_currentView将指向同一块内存。

只想为nacho4d的答案添加一点。 如果你NSLog一个解除分配的对象,有时它会崩溃,有时它不会崩溃。 当对象被释放时,所有发生的事情都会被重新添加到空闲内存块列表中。 内存的实际内容仍然看起来像一个对象,直到部分或全部块被重用,向它发送消息通常可以工作。

当您对已释放的对象进行NSLog时,可能会发生以下三种情况之一。

  • 它可以记录,好像它还活着
  • 如果它完全在同一个地方开始,则完全不同的对象可能会响应
  • 你得到EXC_BAD_ACCESS

无论哪一个发生在很大程度上都是机会问题。

您正在声明属性,但您没有使用它,而是直接使用实例变量。 此外,您无法保留变量指向的内存。

看起来您在类中声明了一个实例变量:

@interface MyClass : NSObject {
    UIView * _currentView;
}

@end

你正在做的是你直接访问它,而不使用该属性。 当您分配内存时,您没有保留内存,这意味着您将完全释放内存并将其删除。 为了使它以这种方式工作,你可以这样做:

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

稍后您需要释放_currentView中保留的对象:

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

(如果要为_currentView分配新值,请注意您还需要执行此操作)

另一种方法是实际使用您声明的属性,而是使用retain属性:

@property(nonatomic, retain)UIView *currentView;

要使其可访问,您需要在类实现中进行综合:

@implementation MyClass 

@synthesize currentView = _currentView;

/*...*/

@end

这将使您的保留得到处理。 此外,您不需要考虑释放存储在变量中的先前值,因为这将为您处理。 但是,您需要以这种方式访问​​该属性:

   self.currentView

您的代码示例如下所示:

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

正如您在打印输出中看到的那样,这两个变量仍然指向相同的内存。 不同的是,由于您保留了内存,因此会添加一个保留计数器。 在保留计数器达到0之前,内存不会被释放,这意味着每次保留时都需要释放一次。 同样在后一种情况下,您需要在dealloc方法中释放retain:

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

至于你最后一个问题这一行

v1 = nil;

只会影响v1指向的地址。 它不会影响变量_currentView,也不会影响它指向的内存。

暂无
暂无

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

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