1

我有一个财产:

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

我认为_currentViewandv1都指向同一个记忆。用于v1重新生成对象,并用于_currentView打印对象时,它会崩溃。我可以理解这一点。

但是,如果在v1release 之后和 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)

为什么调用v1release 和 log后_currentView,它会打印

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

5 回答 5

2

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

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

@property(nonatomic, assign)UIView *currentView;

你声明一个 ivar 是assign虽然在这种情况下这无关紧要,因为你没有使用self.currentViewor [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中发送方法tonil是无害的,使用nil作为其他方法的参数是另外一回事

另一件事:

即使你写self.currentView = v1;而不是_currentView = v1;结果也是一样的,因为正确地被声明为赋值。

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

并非所有声明为保留的时间属性都是解决方案,这是逐案的。我建议阅读更多关于 Obj-c 中的内存管理的内容(至少this),还有一些关于 C 指针的内容,然后是关于 ARC 的内容

于 2012-05-30T08:21:23.817 回答
1

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

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

我希望这可以为您澄清事情。

亲切的问候,

于 2012-05-30T07:37:38.633 回答
0

问题是您如何声明该属性:

@property(nonatomic, assign)UIView *currentView;

它应该是:

@property(nonatomic, retain)UIView *currentView;

当您尝试 时NSLog,它将具有垃圾价值,因为您之前释放了它:

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

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

于 2012-05-30T07:31:02.783 回答
0

只想在 nacho4d 的答案中加一分。如果你NSLog是一个释放的对象,有时它会崩溃,有时它不会。当对象被释放时,所发生的一切就是它被重新添加到空闲内存块列表中。内存的实际内容仍然看起来像一个对象,并且在部分或全部块被重用之前,向它发送消息通常可以正常工作。

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

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

无论发生哪种情况,很大程度上都是机会问题。

于 2012-05-30T10:27:28.980 回答
0

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

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

@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 分配新值,也需要这样做)

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

@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 方法中释放保留:

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

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

v1 = nil;

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

于 2012-05-30T11:17:58.823 回答