【问题标题】:When to override objective c getters何时覆盖目标 c 吸气剂
【发布时间】:2012-03-21 02:25:59
【问题描述】:

在过去的一年中,我第一次与其他人一起参与了一些 Objective-C 项目。

偶尔(并且越来越多)我看到其他人重写 getter/accessor 方法,并在此方法中包含实现代码!对我来说,这是一个疯狂的小镇,因为这是拥有一个 setter 的全部意义......这也意味着在 setter 中设置的属性将在 getter 中被覆盖,因此毫无意义。

是这些人行为不端,还是我错过了什么?是否需要覆盖合成属性的 getter 方法?

例子:

@synthesize width;

- (CGFloat)width {
  NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:@"USER_WIDTH"];

  if (userWidth != nil) 
  {
    width = [NSNumber floatValue];
  }     

  //Shouldn't the above lines be done in the SETTER? I have SET the property!
  //Why would I ever need to write anything BUT the below line??       
  return width;
}

- (void)setWidth:(CGFloat)newWidth {
  //THIS is where you do the the setting!
  width = newWidth;
}

更新:

好的宽度是一个不好的例子。太多人陷入了“变量是什么”和“不包括 get 在objective-c 访问器中”的语义上。所以我更新了上面的例子,忽略了不相关的语义,专注于概念。这个概念是......当你想要覆盖 GETTER (不是setter,只有getter。我多次覆盖setter,这个问题是关于getter的)时,是否有任何例子? p>

返回另一个属性,例如层(如下所述)是一个真实的例子。但更具体地说,是否需要在 GETTERSET 属性?这是我看到的一些奇怪之处,所以我更新了上面的 getter 以从 NSUserDefaults 中提取一个值来帮助我的观点......

【问题讨论】:

  • 你能在标题中添加宽度是如何定义的吗?

标签: objective-c ios xcode properties getter-setter


【解决方案1】:

首先,Cocoa 命名约定将 getter 称为 -width,而不是 -getWidth。 “Get”用于填写传入的参数:

- (void) getWidth:(CGFloat *)outWidth
{
    if (outWidth) *outWidth = _width;
}

也就是说,回到你原来的问题:

在过去,在 @property@synthesize 之前,我们必须像上面那样手动编写访问器。

但是,在其他情况下,您可能希望手动编写访问器。

一个常见的方法是延迟初始化,直到需要一个值。例如,假设有一个图像需要一段时间才能生成。每次修改会改变图像的属性时,您都不想立即重绘图像。相反,您可以将抽签推迟到下一次有人询问时:

- (UIImage *) imageThatTakesAwhileToGenerate
{
    if (!_imageThatTakesAwhileToGenerate) {
        // set it here
    } 

    return _imageThatTakesAwhileToGenerate;
} 


- (void) setColorOfImage:(UIColor *)color
{
    if (_color != color) {
        [_color release];
        _color = [color retain];

        // Invalidate _imageThatTakesAwhileToGenerate, we will recreate it the next time that the accessor is called
        [_imageThatTakesAwhileToGenerate release];
        _imageThatTakesAwhileToGenerate = nil;
    }
}

另一个用途是将访问器/修改器的实现转发到另一个类。例如,UIView 将其许多属性转发给支持CALayer

// Not actual UIKit implementation, but similar:
- (CGRect) bounds { return [[self layer] bounds]; }
- (void) setBounds:(CGRect)bounds { [[self layer] setBounds:bounds]; }
- (void) setHidden:(BOOL)hidden { [[self layer] setHidden:hidden]; }
- (BOOL) isHidden { return [[self layer] isHidden]; }
- (void) setClipsToBounds:(BOOL)clipsToBounds { [[self layer] setMasksToBounds:clipsToBounds]; }
- (BOOL) clipsToBounds { return [[self layer] masksToBounds]; }

更新到提问者的更新:

在您的更新中,看起来有问题的代码要么尝试使用 NSUserDefaults 保留宽度值,要么尝试允许用户指定自定义值以覆盖所有返回的宽度。如果是后者,你的例子很好(尽管我会限制这种做法,因为它可能会给新手造成混淆)。

如果是前者,您希望从 NSUserDefaults 加载一次值,然后在 setter 中将新值保存回 NSUserDefaults。例如:

static NSString * const sUserWidthKey = @"USER_WIDTH";

@implementation Foo {
    CGFloat _width;
    BOOL _isWidthIvarTheSameAsTruthValue;
}

@synthesize width = _width;

- (CGFloat) width
{
    if (!_isWidthIvarTheSameAsTruthValue) {
        NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:sUserWidthKey];
        if (userWidth != nil) _width = [NSNumber doubleValue];
        _isWidthIvarTheSameAsTruthValue = YES;
    }

    return _width;
}

- (void) setWidth:(CGFloat)newWidth
{
    if (_width != newWidth) {
        _width = newWidth;
        NSNumber *userWidthNumber = [NSNumber numberWithDouble:_width];
        [[NSUserDefaults standardUserDefaults] setObject:userWidthNumber forKey:sUserWidthKey];
        _isWidthIvarTheSameAsTruthValue = YES;
    }
}

@end

_width ivar 被用作缓存。真相存储在 NSUserDefaults 中。

注意:我在这个例子中使用了 NSUserDefaults,因为你在你的例子中使用了它。在实践中,我更喜欢不将 NSUserDefault 与我的访问器混合使用 ;)

【讨论】:

  • 感谢您的意见。层示例是我一直在寻找的最接近的答案,并且确实是覆盖 getter 的良好候选者。尽管如此,从技术上讲,这并不是在 GETTER 方法中设置属性(这是有问题的概念)......是否曾经有过这样做是有益的还是不好的做法?
  • 更新后的代码与您看到的完全一样吗?在延迟初始化时,在 getter 中设置 ivar 很常见(请参阅我的 -imageThatTakesAwhileToGenerate 示例),因此不一定是“坏的”。但是,您粘贴的示例存在问题。让我在我的帖子中添加另一个示例来尝试显示 NSUserDefaults 的用法。
  • 我仍然认为您没有得到我的要求,请尽量不要过于复杂。我在问 - 在 GETTER 中设置任何东西(忽略这使用 NSUserDefaults,纯粹是假设的事实)是最佳做法吗?因为对我来说,上面的 -(CGFloat)width 方法中的所有内容(除了返回)都可以放在 setWidth 方法中......
  • 在一个非常简单的层面上:不,在 getter 中设置值不是标准。我和其他人在此页面上提供的所有示例都是您可以根据需要使用的工具。最终,最佳实践是发布没有错误的版本;)如果您正在与更初级的 Obj-C 工程师打交道,我发现最好保持 getter/setter 简单并以显式方法加载(-loadValuesFromNSUserDefaultsNow)。但是,请记住,此页面上的所有模式都可以并且确实在系统框架中使用。
  • 这是一个非常外交的临时答案;)感谢您的意见。也许 SO 不是提出灰色的、与设计相关的问题的最佳场所,最好留给提出非黑即白的实施问题。干杯!
【解决方案2】:

第一个问题是您不想使用 getWidth。 objC 中的模式是 name 和 setName。不要使用 getName。它搞乱了绑定和 KVO。

此外,如果只是设置/获取 iVar,则没有理由重写。如果您要进行额外的处理/验证,则可以覆盖。

编辑:

您还应该尽量避免在 getter 中设置数据和进行繁重的处理。 getter 应该封装一些状态并返回数据。期望它的重量很轻。繁重的处理和/或修改应在方法或设置器中完成。例如,人们在 getter 上设置调试监视,并且不期望对状态进行繁重的处理和修改。

【讨论】:

  • 谢谢。作为记录,我从未将 get 附加到访问器名称中,这只是深夜语义。让我们不要从有问题的概念上分心......也就是说,是否需要在 GETTER 方法中设置属性?
  • 延迟实例化是您可能希望自己实现 getter 的模式示例。
【解决方案3】:

如果你懒惰地创建你的属性对象呢? 我经常使用这个模式,也用在 Xcode 的 CoreData 模板等中。

- (NSString *)string
{
    if (!_string) {
        // Create the string property lazily
        // Create is using some other internal, etc values
        _string = [NSString alloc] initWith...]
    }
    return _string;
}

还有:

- (void)setString:(NSString *)string
{
    if (![string isEqualToString:_string]) {
        // Probably you want to make your property observable here too :)

        [_setString release];
        _setString = [string retain];

        // Update other things that depend on _string for example redraw the view, etc
        [self setNeedsDisplay];
    }
}

【讨论】:

    【解决方案4】:

    重写 getter 和 setter 方法的原因有很多,例如我在创建自定义 UITableViewCell 对象时重写了 setter 方法,这样我就可以设置一个属性,然后在该 setter 方法中,它会自动更新一个标签或单元格内的东西,而不是我必须在设置属性后调用更新函数。

    如果您想以不同于接收信息的方式存储信息,您可能需要覆盖 getter,例如电话号码对象,您可以将其存储为 5551234567,它会自动检索为 555-123-4567 或类似的东西。我自己很少重写 getter 方法,但我经常重写 setter 方法。

    【讨论】:

    • 当您有使用自定义数据对象的自定义 UI 元素时,这尤其有用,这就是为什么我不只是自己制作 UI 组件属性,更容易使用数据对象并覆盖二传手。
    • 这里不考虑覆盖设置器。覆盖 GETTER 来设置属性是个问题...
    【解决方案5】:

    这是面向对象编程中完全可以接受的做法。但是,需要注意副作用。例如,您不应该在 setter 方法中执行网络访问之类的操作。

    但是,在您上面列出的代码中,由于它们与合成方法所做的没有任何不同,因此没有理由包含实现。他们只是把代码弄得乱七八糟。

    【讨论】:

    • 你的第一段有可信度。您可以在这里找到一些东西,作为一个真正的例子,您应该在 GETTER 方法中设置属性。请不要被上面的代码分心,这只是我脑海中的一个深夜例子。无论如何,我已经更新了代码示例以更好地解释相关概念。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    相关资源
    最近更新 更多