【问题标题】:Which one is initialized, property or its instance variable初始化哪个,属性或其实例变量
【发布时间】:2013-10-29 01:41:31
【问题描述】:

假设我在我的类MyClassName 中定义了一个名为myPropertyName 的属性。这篇文章通篇使用手动内存管理。

MyClassName.h

#import <UIKit/UIKit.h>
@interface MyClassName : NSObject {
    @private
        NSObject* myPropertyName;
    @public
}
@property (nonatomic, retain) NSObject* myPropertyName;
// Some methods prototypes are here
@end

MyClassName.m

#import "MyClassName.h"
@implementation MyClassName
@synthesize myPropertyName;
// Some methods are here
@end

我对 myPropertyName 声明的位置、实例变量之间的区别等用法感到困惑。比如初始化代码这三个语句有什么区别,比如我的类myClassName自定义的-(void)init方法中。

  1. self.myPropertyName = [[[NSObject alloc] init] autorelease];

    这个调用myPropertyName setter,但我不确定setter中使用的实例变量的名称是什么,myPropertyName(因为我已经声明了一个名为myPropertyName的@private字段)还是_myPropertyName(有人说这个带下划线的是默认的)?

  2. myPropertyName = [[NSObject alloc] init];

    这会初始化myPropertyName 属性的实例变量吗?如果我没有@synthesize myPropertyName = _myPropertyName;,会不会出错,因为属性的默认实例变量据说是_myPropertyName

  3. _myPropertyName = [[NSObject alloc] init];

    即使我使用@synthesize myPropertyName;@private NSObject* myPropertyName;_myPropertyName 是否仍被声明为我的属性myPropertyName 的实例变量?

在我的理解中,属性只是一个名字(如myPropertyName),代码中应该有一些实例变量被封装后用于实际操作,如赋值等。

【问题讨论】:

  • 您不再需要@synthesize。关于似乎是您的实际问题,您可以有一个 @property 而没有相应的 ivar

标签: objective-c properties ivar


【解决方案1】:

首先,我强烈推荐阅读Apple's documentation on properties,也由 nhgrif 链接。不过,我知道文档可能有点密集阅读材料(尽管我发现 Apple 的阅读材料还不错),所以我将在此处简要概述属性。

我喜欢例子,所以我打算用更现代的形式重写你的两个类。

MyClassName.h

#import <UIKit/UIKit.h>
@interface MyClassName : NSObject

@property (nonatomic, strong) NSObject *myPropertyName;
// method prototypes here
@end

MyClassName.m

#import "MyClassName.h"
@implementation MyClassName
// some methods here
@end

MyClassName 现在有一个名为myPropertyName 的属性,类型为NSObject *。在这种情况下,编译器将为您“免费”做很多工作。具体来说,它将生成一个支持变量,并为myPropertyName 生成一个setter 和getter。如果我要重写这两个文件,并假装我是编译器,包括那些东西,它们看起来像这样:

MyClassName.h

#import <UIKit/UIKit.h>
@interface MyClassName : NSObject {
    NSObject *_myPropertyName;
}

@property (nonatomic, strong) NSObject *myPropertyName;

- (void)setMyPropertyName:(NSObject *)obj;
- (NSObject *)myPropertyName;

@end

MyClassName.m

#import "MyClassName.h"
@implementation MyClassName

- (void)setMyPropertyName:(NSObject *)obj
{
    _myPropertyName = obj;
}

- (NSObject *)myPropertyName
{
    return _myPropertyName;
}

@end

再一次,所有这些都是“免费”发生的:我只是向您展示幕后发生的事情。现在为您编号的问题。

  1. self.myPropertyName = [[[NSObject alloc] init] autorelease];

    首先,您可能应该使用自动引用计数或 ARC。如果是,您将无法拨打autorelease。忽略那部分,这很好用。不包括autorelease,这完全等同于:

    [self setMyPropertyName:[[NSObject alloc] init]];

    如果你看一下我在上面写的第二个.m文件,基本上会翻译成:

    `_myPropertyName = [[NSObject alloc] init];

  2. myPropertyName = [[NSObject alloc] init];

    正如所写,此代码将给出编译器错误,因为此类中没有名为myPropertyName 的变量。如果您真的想访问(或“支持”)myPropertyName 属性的实例变量,您可以使用其真实名称:

    _myPropertyName = [[NSObject alloc] init]; // note the underscore

    但在大多数情况下,最好使用 setter,如第 1 点所示,因为这会产生副作用,以及键值编码和其他好东西。

  3. _myPropertyName = [[NSObject alloc] init];

    哦。好吧,你明白了。见第 2 点。

你提到过:

我对 myPropertyName 声明的位置、实例变量之间的区别等用法感到困惑。比如初始化代码这三个语句有什么区别,比如我的类myClassName自定义的-(void)init方法中。

如果没有说清楚,属性是一个抽象概念;它的数据存储在一个普通的实例变量中,通常由编译器分配。它的访问权限通常应仅限于 setter 和 getter,但有重要例外。为了让这个答案简短,我不会再详细说明了。

还有一件事:正如 nhgrif 所说,您不再需要使用 @synthesize 关键字。编译器现在隐含地理解了这一点。

如果您对此有任何不确定,请发表评论,或者更好的是阅读docs

【讨论】:

  • 致@Riley,在你的这句话中 "如写的那样,这段代码将给出编译器错误,因为在这个类中没有名为 _myPropertyName 的变量。如果你真的想访问实例作为 myPropertyName 属性的基础(或“支持”)的变量,您可以使用其真实名称。” 你的意思是“这里没有名为 myPropertyName 的变量类”?
  • @conglu 我确实做到了。非常感谢您指出这一点。我的答案已被编辑。
【解决方案2】:

我们来看这个例子:

@property NSString *fullName;

如果在实现中,我们覆盖了 setter 和 getter,并且在这些 setter 和 getter 中,我们不使用实例变量fullName,它永远不会被创建。例如:

- (NSString *)fullName
{
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

- (void)setFullName:(NSString *)fullName
{
    //logic to split fullName into two strings
    //self.firstName = etc
    //self.lastName = etc.
}

在此示例中,没有为 fullName 创建实例变量。

这是根据Apple's Official Documentation

但是,如果您不同时覆盖 setter 和 getter,则会创建一个实例变量。

作为旁注,您可以声明一个属性readonly,然后简单地覆盖getter(不使用变量)将阻止创建ivar。同样,您可以声明属性writeonly 并覆盖setter

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 2017-04-05
    • 2011-03-18
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    相关资源
    最近更新 更多