这有点棘手。我将我的答案总结为 5 个部分:
- 创建
init
返回不同对象的自定义方法
- 警告:当心非法内存访问!
- 如何正确地将按钮的所有权转移到其父视图
- 具体问题的具体答案
- 改进建议
第 1 部分:创建init
返回不同对象的自定义方法:
这是一个非常特殊情况的示例,即返回的-initWithTitle:frame:
对象与最初发送消息的“对象”不同。
通常来说,这个过程是这样的:
instance = [Class alloc];
[instance init];
...
[instance release];
当然,alloc 和 init 调用通常组合在一起成为一行代码。这里要注意的关键是已经分配了接收到 init 调用的“对象”(此时只不过是分配的内存块)。如果您返回不同的对象(如您的示例中所示),则您有责任释放该原始内存块。
下一步是返回一个具有正确保留计数的新对象。由于您使用的是工厂方法 ( +buttonWithType:
),因此生成的对象已被自动释放,您必须保留它以设置正确的保留计数。
编辑:一个正确的-init
方法应该明确地测试以确保它在对一个正确初始化的对象做任何其他事情之前,它正在使用该对象。我的答案中缺少此测试,但出现在bbum 的答案中。
这是您的 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
有一些 ivars:
@class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
@end;
现在,当您用自定义方法中CustomButton
的实例替换分配的内存时,您返回的内存块太小而无法容纳 a ,但您的代码将继续将此代码块视为全尺寸. 哦哦。UIButton
init...
CustomButton
CustomButton
例如,下面的代码现在非常非常糟糕:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self retain]; // set the proper retain count
someOtherVar = 10; // danger, Will Robinson!
return self;
}
第 3 部分:如何正确地将按钮的所有权转移到其父视图:
至于您的视图控制器的方法,如果您已初始化按钮,dealloc
则必须调用,如图所示。[myButton release]
这是为了遵循您必须释放您分配、保留或复制的任何内容的规则。处理此问题的更好方法是让控制器的视图拥有该按钮的所有权(当您将按钮添加为子视图时它会自动执行此操作):
myButton = [[CustomButton alloc] initWithTitle:@"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1
现在,您再也不用担心再次释放该按钮了。视图拥有它,并在视图本身被释放时释放它。
第 4 部分:具体问题的具体答案:
问:按照我的理解,myButton 实际上并没有保留,即使我使用 alloc 调用它,因为在我的子类中我创建了一个自动释放按钮(使用 buttonWithType:)。
正确的。
问:在dealloc中,这是否意味着,当调用dealloc时,superview释放按钮并且它的retain count下降到1?该按钮尚未自动释放?
也正确。
问:或者我是否需要在调用 [super dealloc] 后将保留计数减为零?
在您记录时,:)
保留计数可能会或可能不会降至零。自动释放对象的保留计数可能仍然为 1,因为它们实际上属于当前运行循环的自动释放池。就此而言,视图本身可能仍属于尚未释放的窗口。您真正需要担心的唯一一件事是平衡您自己的内存管理。有关详细信息,请参阅Apple 的内存管理指南。从您的 viewController 的角度来看,您已经分配了一次按钮,因此您必须准确地释放它一次。当涉及到您的定制init...
方法,事情变得有点棘手,但原理是一样的。已经分配了一块内存,因此必须释放它(第 1 部分),并且(第 2 部分)init 应该返回一个保留计数为 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 部分中确定的内存访问错误