Objective c 属性与实例变量

Objective c 属性与实例变量,objective-c,object,properties,ivar,Objective C,Object,Properties,Ivar,我不明白为什么有些类声明属性,但不声明ivar,反之亦然 在声明实例变量时也将其声明为属性,这是标准做法吗 例如: @interface AppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; UINavigationController *navigationController; } @property (nonatomic, retain) IBOutlet UIWindow *wi

我不明白为什么有些类声明属性,但不声明ivar,反之亦然

在声明实例变量时也将其声明为属性,这是标准做法吗

例如:

@interface AppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
    UINavigationController *navigationController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
@接口AppDelegate:NSObject
{
UIWindow*窗口;
UINavigationController*导航控制器;
}
@属性(非原子,保留)IBUIWindow*window;
@属性(非原子,保留)IBuinavigationController*导航控制器;
在声明类的ivar以使其也成为属性时,这仅仅是标准实践吗

我知道@property创建了自己的拉长setter(和带有@synethesize的getter),但是为什么它也需要是ivar呢

没有

在过去,IVAR需要在
@界面中声明。对于PPC和i386(即32位intel)目标,这实际上仍然是正确的。这是因为,这要求所有子类都知道其超类的确切大小。因此,ivar需要位于
@接口中
,否则没有人可以对该类进行子类化

随着迁移到x86_64和ARM,与obj-c 2.0一起解决了脆弱的基类问题。使用此修复程序,类大小不再需要在编译时知道,而是可以推迟到运行时。因此,可以在其他地方申报IVAR。值得注意的是,现在可以从
@属性
合成ivar(更具体地说,是实现中的
@synthesis
行)。在Clang中,它们也可以在类扩展块(看起来像
@interface ClassName()
)中声明,或者直接在
@实现中声明

今天,您发现在
@interface
块中声明IVAR的原因有三个:

  • 旧代码(或有旧习惯的程序员)尚未更新以利用隐藏ivar声明的能力
  • 需要在PPC或i386上运行的代码
  • 无论出于何种原因,代码都希望其IVAR公开。这种情况永远不应该发生

  • 今天编写不需要以旧运行时为目标的代码时,您应该从属性合成IVAR(首选),或者如果您需要与属性无关的IVAR,则应该在类扩展或
    @实现中声明它们。这主要是因为头文件记录了类的公共API,不应该包含任何非公共的内容。IVAR不是公共的,因此不应该出现在头文件中。

    或者,如果您想更有趣,可以使用
    OBJC_IVAR
    :感谢您的回复。问:听起来,将iVar与属性一起声明是一种过时的方法。但是什么时候应该声明实例变量而不是属性?只是为了结束。谢谢@GPP:这多少是个人喜好的问题。一般来说,我认为obj-c对象应该始终是属性。但是对于C数据类型,您可能决定只直接使用IVAR。或者您可能更喜欢使用属性。这取决于你。请注意,如果需要“private”属性,则应在.m文件的类扩展名中声明它。@denis631这意味着编译器可以根据类的实现合成IVAR。在“脆弱”体系结构上,必须在
    @interface
    块中声明IVAR,因为所有子类都需要知道超类内存布局的精确大小/对齐方式。在“现代”体系结构中,情况不再如此,因此IVAR不再需要放入公共头文件中。现在可以通过包含ivar偏移量的单独符号访问ivar。因此,是的,偏移量基本上是在运行时确定的。@denis631每个ivar都会在
    \uUjc\uIVAR
    部分的
    数据中获得一个隐藏符号,形式为
    OBJC\uIVAR\u$\ u ClassName.ivarName
    。当obj-c运行时初始化类时,它会使用ivar从类开始的偏移量更新每个ivar符号的值。然后重写对ivar的每次访问,以使用该符号作为偏移量,而不是编译时值(尽管编译器知道偏移量在第一次访问后永远不会改变,因此可以优化冗余负载)。