[英]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);
我认为_currentView
和v1
都指向相同的内存。 当使用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 <UIView: 0x81ccbc0; frame = (0 0; 0 0);
transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)>
这不一定与@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时,可能会发生以下三种情况之一。
无论哪一个发生在很大程度上都是机会问题。
您正在声明属性,但您没有使用它,而是直接使用实例变量。 此外,您无法保留变量指向的内存。
看起来您在类中声明了一个实例变量:
@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.