是否需要在 UIViewController 中声明 UIButton 属性 作为 UIKit 中的一个错误?
Is the need to declare a UIButton property in a UIViewController as strong a bug in UIKit?
我有一个相当基本的问题,我实际上不确定它是 UIKit
中的错误还是预期的行为。
在 UIViewController
中声明视图属性时,似乎普遍同意应该将其添加到视图控制器的 subviews
中并因此显示在屏幕上,这些属性应该是 weak
.
此 weak
声明背后的基本原理是有道理的,因为视图控制器已经通过其 subviews
属性 拥有子视图,因此另一个强引用不会在此处添加任何值。许多 Whosebug 帖子也证实了这一点,例如this one.
我现在 运行 遇到 UIButton
的问题。当我想以编程方式添加一个按钮并首先将其声明为 UIViewController
的 属性 然后调用 [self.view addSubview:self.someButton]
时,该按钮仅在声明为 strong
时显示,但当被声明为 weak
.
时不是
这是理解我的问题的最简单的示例代码:
@interface ButtonTestViewController ()
// only works with strong!
// when declared weak, no button appears on the screen and the
// logging output doesn't contain the button as a subview...
@property (nonatomic, strong) UIButton *someButton;
@end
@implementation ButtonTestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.someButton setCenter:self.view.center];
[self.view addSubview:self.someButton];
NSLog(@"subviews: %@", self.view.subviews);
}
- (UIButton *)someButton
{
if (!_someButton) {
UIButton *someButton = [UIButton buttonWithType:UIButtonTypeSystem];
CGRect someButtonFrame = CGRectMake(0.0, 0.0, 100.0, 44.0);
someButton.frame = someButtonFrame;
[someButton setTitle:@"Do something" forState:UIControlStateNormal];
[someButton addTarget:self action:@selector(someButtonPressed) forControlEvents:UIControlEventTouchUpInside];
_someButton = someButton;
}
return _someButton;
}
- (void)someButtonPressed
{
NSLog(@"button pressed...");
}
@end
我还设置了一个小的 gist,我还在 UIButton
旁边添加了另一个 UI 元素(一个 UITextField
)以进行比较。 UITextField
在声明 weak
时也会显示。那么,这是 UIKit
中的错误还是实际上有一个原因导致 UIButton
无法声明 weak
?
网点被创建为弱的事实主要有两个原因:
- 没有必要将它们创建为
strong
因为它们已经被它们的超级视图所拥有
- 曾几何时,在 -
viewDidUnload
中通常使用 nil 来避免在内存警告后创建僵尸,现在您可以免费获得它。如果视图消失了,你已经拥有了它们 nil
reatain循环不是这样创建的,它是一个对象A保留B,保留A创建一个retain cycle,按钮本身没有保留。
你的问题是因为如果你直接将按钮实例添加到一个弱变量,在将它添加到保留所有权的人之前,它会立即被释放。
您可以将按钮创建为 weak
,但您应该以另一种方式添加值:
- 创建简单的局部变量,例如
UIButton *someButton = [UIButton whatever]
- 将其作为子视图添加到您的视图中
- 现在您可以安全地传递对
weak
变量的引用,因为该按钮归视图所有
当你不使用 Interface Builder 时,你不能让子视图变弱。你必须在你的情况下使用 strong 。否则按钮在添加到视图之前被释放。
我有一个相当基本的问题,我实际上不确定它是 UIKit
中的错误还是预期的行为。
在 UIViewController
中声明视图属性时,似乎普遍同意应该将其添加到视图控制器的 subviews
中并因此显示在屏幕上,这些属性应该是 weak
.
此 weak
声明背后的基本原理是有道理的,因为视图控制器已经通过其 subviews
属性 拥有子视图,因此另一个强引用不会在此处添加任何值。许多 Whosebug 帖子也证实了这一点,例如this one.
我现在 运行 遇到 UIButton
的问题。当我想以编程方式添加一个按钮并首先将其声明为 UIViewController
的 属性 然后调用 [self.view addSubview:self.someButton]
时,该按钮仅在声明为 strong
时显示,但当被声明为 weak
.
这是理解我的问题的最简单的示例代码:
@interface ButtonTestViewController ()
// only works with strong!
// when declared weak, no button appears on the screen and the
// logging output doesn't contain the button as a subview...
@property (nonatomic, strong) UIButton *someButton;
@end
@implementation ButtonTestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.someButton setCenter:self.view.center];
[self.view addSubview:self.someButton];
NSLog(@"subviews: %@", self.view.subviews);
}
- (UIButton *)someButton
{
if (!_someButton) {
UIButton *someButton = [UIButton buttonWithType:UIButtonTypeSystem];
CGRect someButtonFrame = CGRectMake(0.0, 0.0, 100.0, 44.0);
someButton.frame = someButtonFrame;
[someButton setTitle:@"Do something" forState:UIControlStateNormal];
[someButton addTarget:self action:@selector(someButtonPressed) forControlEvents:UIControlEventTouchUpInside];
_someButton = someButton;
}
return _someButton;
}
- (void)someButtonPressed
{
NSLog(@"button pressed...");
}
@end
我还设置了一个小的 gist,我还在 UIButton
旁边添加了另一个 UI 元素(一个 UITextField
)以进行比较。 UITextField
在声明 weak
时也会显示。那么,这是 UIKit
中的错误还是实际上有一个原因导致 UIButton
无法声明 weak
?
网点被创建为弱的事实主要有两个原因:
- 没有必要将它们创建为
strong
因为它们已经被它们的超级视图所拥有 - 曾几何时,在 -
viewDidUnload
中通常使用 nil 来避免在内存警告后创建僵尸,现在您可以免费获得它。如果视图消失了,你已经拥有了它们 nil
reatain循环不是这样创建的,它是一个对象A保留B,保留A创建一个retain cycle,按钮本身没有保留。
你的问题是因为如果你直接将按钮实例添加到一个弱变量,在将它添加到保留所有权的人之前,它会立即被释放。
您可以将按钮创建为 weak
,但您应该以另一种方式添加值:
- 创建简单的局部变量,例如
UIButton *someButton = [UIButton whatever]
- 将其作为子视图添加到您的视图中
- 现在您可以安全地传递对
weak
变量的引用,因为该按钮归视图所有
当你不使用 Interface Builder 时,你不能让子视图变弱。你必须在你的情况下使用 strong 。否则按钮在添加到视图之前被释放。