【问题标题】:Simple Class Extension / Inheritance Clarification简单的类扩展/继承说明
【发布时间】:2014-10-31 17:54:59
【问题描述】:

我已经编写 Objective-C 几年了,并决定回去学习基础知识,以帮助我编写更好的代码。我正在尝试学习所有关于实例变量、继承和类扩展的知识。我一直在阅读这三个方面的内容,但有一件事让我大吃一惊。我有一个简单的应用程序,其中包含 2 个类,Person、Male(从 Person 继承),当然还有 Main(它导入 Male 类,因此能够访问 Person 和 Male 中的实例变量)。

代码很简单,为了篇幅我就不一一贴出来了。基本上 Main 采用这些变量并使用它们。这是让我难以置信的部分:

    @interface Person : NSObject {
    float heightInMeters;
    int weightInKilos;
}

@property float heightInMeters;
@property int weightInKilos;

@end

当我删除括号和变量声明时,保留如下:

@interface Person : NSObject

@property float heightInMeters;
@property int weightInKilos;

@end

代码仍然继承和执行得很好。

1.如果我们可以只创建两个属性,那么一开始就在那里声明它们有什么意义?

2.为什么要创建两个实例变量和属性来对应它们?

3. 我知道我们可以在 .m 中声明变量,而不是让它们对类及其子类的所有内容都是私有的。像这样:

    @implementation Person {
    float heightInMeters;
    int weightInKilos;
    }

这里有什么区别?我觉得我错过了很多基础知识。有没有一种简单的方式来看待这一切?

【问题讨论】:

  • Xcode 4.4 中添加了“自动属性合成”,所以你的第二个版本很好,在大多数情况下你不必声明实例变量。参见例如stackoverflow.com/questions/9368676/…
  • @MartinR 这很有趣......谢谢你的例子。并且在一些相关的混淆说明中...... heightInMeters 和 weightInKilos 都是实例变量而不是类变量,对吗?
  • 是的。准确地说,如果您声明@property float heightInMeters;,那么编译器将“合成”一个相应的实例变量_heightInMeters(带有下划线)。但是已经有很多围绕该主题的问答,如果您搜索“属性与实例变量”,您应该会找到所需的一切。
  • Objective-C 2.0 中还添加了非脆弱实例变量。这消除了在 @interface { } 块中声明所有内容的需要

标签: objective-c inheritance class-extensions


【解决方案1】:

当你声明一个@property时,编译器会自动合成带有下划线前缀的变量,一个getter方法,一个setter方法。

@interface MyClass () 

@property(strong, nonatomic) NSString *myString;

@end

在这个例子中,编译器会将变量syhtnesize为_myString,getter为

-(NSString *)myString

设置器为

-(void)setMyString:(NSString *)string

“@property”(strong, nonatomic) 之后的关键字定义了属性的属性。 strong,默认值,意味着所有权,这意味着在这种情况下,MyClass 实例将基本上负责它们各自的 myString 对象的保留/释放。 nonatomic 表示在多线程环境中不能保证该变量始终是有效值,例如,如果 getter 与 setter 同时被调用。

此外,编译器会将用于检索/设置实例变量的点语法视为对相应 getter/setter 方法的调用。因此,给定一个 MyClass 的实例

MyClass *exampleClass = [[MyClass alloc] init];

以下两个是等价的语句:

NSString *string1 = example.myString;   // dot syntax
NSString *string1 = [example myString]; // explicit call to the getter method

如需进一步阅读,请查看 Apple 的 Programming with Objective-C Guide

至于你的具体问题:

1。如果我们只能创建两个属性,那么一开始就在那里声明它们有什么意义?

在您的MyClass.h 文件中(或在大多数其他情况下)declare variables explicitly as public variables 实际上不是一个好主意。相反,将它们声明为属性会自动创建一个私有变量(和访问器方法),从而更容易遵循 OOP 最佳实践。所以没有必要声明

// MyClass.h

@interface MyClass : NSObject {
    NSString *myString // public variables not good
}

同样由于我上面提到的点语法,如果你在内部使用self.myString,在内部使用MyClass.m 或在外部使用instanceOfMyClass.myString,公共变量myString 将永远不会被触及,因为合成变量被命名为@987654342 @。

2。为什么要创建两个实例变量和属性来对应它们?

见上文——您不需要两个实例变量,只需要一个。

3。我知道我们可以在 .m 中声明变量,而不是让它们对类和子类的所有内容都是私有的。这里有什么区别?我觉得我错过了很多基础知识。有没有一种简单的方式来看待这一切?

如果您在.m 文件的@implementation 部分中私下声明变量,编译器将无法通过合成getter 和setter 来帮助您。即使作为私有方法,getter 和 setter 也可以帮助降低代码的复杂性,例如检查变量值的有效性。 (注:你可以override accessor methods。)

// MyClass.m

@interface MyClass () // private interface

@property(nonatomic, strong) NSString *myString;

@end


@implementation MyClass {
    // no more need for private variables!
    // compiler will synthesize NSString *_myString and accessors
}

-(void)setMyString:(NSString *)string { // overwrite setter

    // no empty strings allowed in our object (for the sake of example)
    NSAssert([string length] > 0, @"String must not be empty");

    // assign private instance variable in setter
    _myString = string;
}

@end

这样,即使您将MyClass 子类化,子类也会继承编译器为我们合成的getter 和setter 方法。

【讨论】:

  • 感谢您的帮助,它提供了很好的洞察力。我还用粗体表示了三个特定于实例变量和类扩展的问题,您能提供任何见解吗?
  • 你太棒了,非常感谢!非常有帮助。因此,在 .m 中声明私有变量的唯一原因主要是如果您想手动创建 getter 和 setter,对吧?
  • 不,您可以为任一文件中声明的属性重新创建 getter 和 setter。通常,在.h 文件中声明为readonly 的属性将在.m 中重新声明为readwrite。编译器不会为readonly 属性合成setter,因此在.m 中重新声明它允许合成私有setter 以供内部使用。还有更多技巧,关于这个主题已经写了很多博客文章。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 2013-09-26
相关资源
最近更新 更多