【问题标题】:Why can't I initialize a class through a setter?为什么我不能通过 setter 初始化一个类?
【发布时间】:2010-05-02 21:13:49
【问题描述】:

如果我有一个名为 Tires 的自定义类:

#import <Foundation/Foundation.h>

@interface Tires : NSObject {
@private
     NSString *brand;
     int size;
}

@property (nonatomic,copy) NSString *brand;
@property int size;

- (id)init;
- (void)dealloc;

@end
=============================================

#import "Tires.h"

@implementation Tires

@synthesize brand, size;

- (id)init {
     if (self = [super init]) {
          [self setBrand:[[NSString alloc] initWithString:@""]];
          [self setSize:0];
     }
     return self;
}

- (void)dealloc {
     [super dealloc];
     [brand release];
}

@end

我在 View Controller 中合成了一个 setter 和 getter:

#import <UIKit/UIKit.h>
#import "Tires.h"

@interface testViewController : UIViewController {
     Tires *frontLeft, *frontRight, *backleft, *backRight;
}

@property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;

@end

====================================

#import "testViewController.h"

@implementation testViewController

@synthesize frontLeft, frontRight, backleft, backRight;

- (void)viewDidLoad {
     [super viewDidLoad];
     [self setFrontLeft:[[Tires alloc] init]];
}
- (void)dealloc {
    [super dealloc];
}

@end

它在 [self setFrontLeft:[[Tires alloc] init]] 回来后死掉。它编译得很好,当我运行调试器时,它实际上一直通过轮胎上的 init 方法,但是一旦它回来它就会死掉并且视图永远不会出现。但是,如果我将 viewDidLoad 方法更改为:

- (void)viewDidLoad {
     [super viewDidLoad];
     frontLeft = [[Tires alloc] init];
}

它工作得很好。我可以放弃 setter 并直接访问 frontLeft 变量,但我的印象是我应该尽可能多地使用 setter 和 getter,从逻辑上讲它看起来像 setFrontLeft方法应该有效。

这带来了我的同事在这些方面不断提出的另一个问题(我们都是 Objective-C 的新手);如果您与那些 setter 和 getter 属于同一类,为什么还要使用 setter 和 getter。

【问题讨论】:

  • 为什么要在同一个类中使用 setter/getter?因为您的 setter(以及很少见的 getter)可能会做一些事情,而不仅仅是分配值,例如管理资源或更新其他实例变量。直接在类中访问变量意味着您必须复制该附加功能,否则可能会失去内部一致性。它破坏了封装。我相信在构造函数中不使用合成属性方法是有原则的,因为它们可能没有完全设置好——但我对内部结构不够了解,无法理解它们是如何失败的。

标签: objective-c setter getter


【解决方案1】:

您已将 frontLeft 声明为“复制”属性:

@property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;

当您分配给此属性时,会通过调用对象的copy 方法进行复制。这仅适用于支持NSCopying 协议的对象(即实现copyWithZone: 方法)。由于您的 Tires 类没有实现此方法,因此您会遇到异常。

您可能希望将其更改为“保留”属性:

@property (nonatomic,retain) Tires *frontLeft, *frontRight, *backleft, *backRight;

有关属性声明的更多信息,请参阅the Objective C documentation on declared properties

【讨论】:

  • 效果很好!我忘记了 setter 调用 copy 方法。将此标记为答案。即使您与它们在同一个类中,对实例变量使用 setter 和 getter 是常见的做法吗?我和我的合作开发者之间一直在讨论这个问题。
  • 我认为你是否使用getter/setter主要是一个偏好问题。在这种情况下,我会使用 setter,因为它会自动为您保留变量。我不会在课堂上使用 getter。
  • 使用 setter 可以免费进行内存管理(假设您已经正确设置了属性),这在大多数情况下是一个巨大的胜利。如果我使用 setter(在一个类中),我通常会使用 getter 以使代码更加一致(因此更具可读性)。
  • 在书籍和示例中,我经常看到它们在单独的行上使用 alloc 然后在之后释放它。因此,他们将首先执行 [self setFrontLeft:[[Tires alloc] init]] 而不是 Tires *myTires = [[Tires alloc] init],然后执行 [self setFrontLeft:myTires] 然后释放 myTires。我尝试这样做的方式是否会导致内存泄漏?
【解决方案2】:

我看到的一个问题是:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setFrontLeft:[[Tires alloc] init]];
}

当您调用 [Tires alloc] 时,您会返回一个保留计数为 1 的对象。然后您使用已合成的 set 方法,它将保留计数增加到 2。当您的对象使用完轮胎对象时,它将保留计数减少回 1,但轮胎永远不会被释放。我认为你应该使用:

[self setFrontLeft:[[[Tires alloc] init] autorelease]];

【讨论】:

  • 试过 [self setFrontLeft:[[[Tires alloc] init] autorelease]]; 它仍然做同样的事情。在调试器中显示 TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION,如果我运行该应用程序,它会在启动后立即崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 2015-03-04
  • 2013-12-31
  • 1970-01-01
相关资源
最近更新 更多