Iphone Dealoc中的自定义UIButton内存管理
我希望澄清这里正在进行的过程 我创建了UIButton的一个子类,其init方法如下所示:Iphone Dealoc中的自定义UIButton内存管理,iphone,ios,memory-management,uibutton,dealloc,Iphone,Ios,Memory Management,Uibutton,Dealloc,我希望澄清这里正在进行的过程 我创建了UIButton的一个子类,其init方法如下所示: - (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame { self = [UIButton buttonWithType:UIButtonTypeCustom]; [self setTitle:title forState:UIControlStateNormal]; self.frame = btnFrame;
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
instance = [Class alloc];
[instance init];
...
[instance release];
在我的视图控制器中,我正在创建其中一个按钮并将其添加为子视图:
myButton = [[CustomButton alloc] initWithTitle:@"Title" frame:someFrame];
[self.view addSubview:myButton];
myButton = [[CustomButton alloc] initWithTitle:@"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1
在视图控制器的dealloc
方法中,我记录按钮的保留计数:
- (void)dealloc {
NSLog(@"RC: %d", [myButton retainCount]); //RC = 2
[super dealloc];
NSLog(@"RC: %d", [myButton retainCount]); //RC = 1
}
按照我的理解,myButton实际上并没有被保留,即使我使用alloc
调用它,因为在我的子类中我创建了一个自动释放按钮(使用buttonWithType:
)
在dealloc
中,这是否意味着,当调用dealloc时,superview会释放按钮,其保留计数下降到1?按钮尚未自动释放?
或者我需要在调用[super dealloc]后将保留计数减为零吗
干杯。这不仅仅是一个小技巧。我将我的答案归纳为五个部分:
init
方法第1部分:创建返回不同对象的自定义
init
方法:
这是一个非常特殊的例子,即从-initWithTitle:frame:
返回的对象不是最初发送消息的同一个“对象”
通常情况下,流程如下所示:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
instance = [Class alloc];
[instance init];
...
[instance release];
当然,alloc和init调用通常被分组到一行代码中。这里需要注意的关键是,接收init调用的“对象”(此时只不过是分配的内存块)已经分配好了。如果您返回一个不同的对象(如示例中所示),您将负责释放该原始内存块
下一步是返回具有适当保留计数的新对象。由于您使用的是factory方法(+按钮类型:
),因此生成的对象已自动删除,您必须保留它以设置正确的保留计数
Edit:一个适当的-init
方法应该在对该对象执行任何其他操作之前,显式测试以确保它正在使用一个正确初始化的对象。这个测试在我的答案中缺失,但出现在我的答案中
以下是init方法的外观:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
if (self == nil) { return nil; }
[self retain]; // set the proper retain count
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
第2部分:警告:小心非法内存访问 如果您正在分配
CustomButton
的实例,然后将其替换为UIButton
的实例,则很容易导致一些非常细微的内存错误。假设CustomButton
有一些IVAR:
@class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
@end;
现在,当您在自定义init…
方法中将分配的CustomButton
替换为UIButton
实例时,返回的内存块太小,无法容纳CustomButton
,但您的代码将继续将此代码块视为一个全尺寸的CustomButton
。哦
例如,下面的代码现在非常非常糟糕:
第3部分:如何正确地将按钮的所有权转移到其父视图: 对于视图控制器的
dealloc
方法,如果您已如图所示初始化按钮,则必须调用[myButton release]
。这是为了遵循规则,即您必须发布您分配、保留或复制的任何内容。处理此问题的更好方法是让控制器的视图拥有该按钮的所有权(当您将该按钮添加为子视图时,它会自动这样做):
现在,你再也不用担心释放那个按钮了。视图拥有它,并将在解除分配视图本身时释放它
第4部分:特定问题的具体答案: 问:按照我的理解,myButton实际上并没有被保留,尽管我使用alloc调用了它,因为在我的子类中,我创建了一个自动释放按钮(使用buttonWithType:) 对 问:在dealloc中,这是否意味着,当调用dealloc时,superview释放按钮,其保留计数下降到1?按钮还没有自动释放 也对 问:还是我需要在调用[super dealloc]后将保留计数降至零 排序
:)
在您记录时,保留计数可能会或可能不会下降到零。自动释放对象的保留计数可能仍然为1,因为它们实际上属于当前运行循环的自动释放池。因此,视图本身可能仍然属于尚未发布的窗口。你唯一真正需要担心的是平衡你自己的内存管理。有关详细信息,请参阅。从viewController的角度来看,您已经分配了按钮一次,因此必须恰好释放它一次。当涉及到定制的init…
方法时,事情会变得有点棘手,但原理是一样的。内存块已经分配,因此必须释放它(第1部分),并且,(第2部分)init应该返回一个retain计数为1的对象(稍后正确释放)
第5部分:改进建议: 您可以通过简单地创建自己的工厂方法来避免大多数自定义初始值设定项混乱,方法的精神与
UIButton
提供的方法相同:
+ (id)buttonWithTitle:(NSString *)title frame:(CGRect)btnFrame {
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
button.frame = btnFrame;
return button;
}
请注意,如第2部分所述,这种方法仍然会导致内存访问错误。这是一个非常棘手的问题。我将我的答案归纳为五个部分:
init
方法<UIButton: 0x9d03fa0; frame = (0 0; 29 31); opaque = NO; layer = <CALayer: 0x9d040a0>>
id x = [Foo alloc]; // allocates instance with RC +1
x = [x init]; // RC is preserved, but x may differ
[x autorelease]; // RC -1 (sometime in future)
- init {
[self release];
self = nil;
id newObject = [SomeClass alloc];
newObject = [newObject init];
if (newObject) {
self = newObject;
... initialize self here, if that is your fancy ...
}
return self;
}