【问题标题】:What is the visibility of @synthesized instance variables?@synthesized 实例变量的可见性是什么?
【发布时间】:2012-08-13 05:41:10
【问题描述】:

如果您的公共界面中有如下属性

@interface MyClass : NSObject
@property(strong) NSString *myProp;
@end

然后合成它,实际上合成了变量:

@implementation MyClass
@synthesize myProp = _myProp; // or just leave it at the default name..
@end

实例变量_myProp 的可见性如何?也就是说,这被认为是@public@protected 还是@private?我猜因为MySubClass 可以从MyClass 继承,那么它也会(自然)获得属性,但它也会继承实例变量的可见性吗?

如果我将属性放在类扩展中会有什么不同?这将隐藏子类的属性,我也在猜测实例变量。这在任何地方都有记录吗?

【问题讨论】:

    标签: objective-c cocoa properties encapsulation


    【解决方案1】:

    如果它在您的界面中声明,则在使用 @property 声明时它实际上是公共的。如果您想使用 @property 声明并保持它们的真正私有属性,您应该在您的实现中创建一个私有类别。

    MyClass.h

    @interface MyClass : NSObject {
    @private
        NSObject* foo;
    }
    @end
    

    MyClass.m

    #import "ClassWithPrivateProperty.h"
    
    @interface MyClass ()
        @property (nonatomic,retain) NSObject* foo; 
    @end
    
    @implementation MyClass
    @synthesize foo;
    // class implementation...
    @end
    

    【讨论】:

    • 好建议,但对 ivar 本身的可见性只字未提。
    • 您的回答并没有真正解决支持 ivar 本身的可见性。
    【解决方案2】:

    合成变量的行为就像声明了@private

    @interface Garble : NSObject
    @property (copy) NSString * s; 
    @end
    @implementation Garble
    @synthesize s;
    @end
    
    @interface Bargle : Garble
    @end
    
    @implementation Bargle
    
    - (void) useS {
        NSLog(@"%@", s);    // error: instance variable 's' is private
    }
    
    @end
    

    我发誓我在the docs 看到过这个,但我现在找不到。如果我找到它会更新。

    【讨论】:

      【解决方案3】:

      合成的 ivar 对所有看不到 @synthesize 行的代码完全不可见(这基本上意味着 .m 文件之外的任何内容)。不是@protected,也不是@private,简直是未知数。使用@private ivar,其他尝试访问它的代码将被告知它是私有的,但对于合成 ivar,其他尝试访问它的代码将被告知该字段根本不存在。

      作为一个思想实验,试着想象一下 ivar 的行为就像是@protected。您创建了一个子类,并在那里与 ivar 混在一起。现在您回到超类并将@synthesize myProp 更改为@synthesize myProp=foo。子类中会发生什么?当编译器处理子类时,它看不到 @synthesize 行,因此它不会知道您只是更改了 ivar 的名称。事实上,它甚至无法判断该属性是否由 ivar 支持,或者它是否使用自定义编写的访问器方法实现。我希望很明显为什么这意味着子类不可能访问 ivar,任何其他类也一样。

      也就是说,如果您在尝试访问 ivar 的同一个 .m 文件中编写代码,我不太确定编译器会做什么。我希望它会将 ivar 视为 @private(因为编译器实际上可以看到 ivar 存在)。

      此外,这些都与运行时方法无关。其他类仍然可以使用 obj-c 运行时方法来动态查找您的类的 ivar 列表并使用它。

      【讨论】:

        【解决方案4】:

        其他类可以访问他们#include 的所有内容。换句话说,就是标题中的所有内容。

        如果某些内容仅出现在您的实现文件中,则其他类(包括子类)不知道它存在。合成属性就是这样。其他类只知道属性(属性表示 getter 和 setter 方法),但对方法的内部实现一无所知。

        请注意,obj-c 中的访问说明符(public/private/protected)只是对编译器的一个提示,即即使某些内容出现在头文件中,也无法访问它。运行时不会以任何方式对其进行检查。

        如果你把它放到类扩展中会发生什么?请注意,属性是一组两个方法。您只需隐藏每个包含类主标题但不包含类扩展标题的类的方法。

        例如,我们使用它来将属性声明为只读,而在类延续中,我们将其声明为读写。然后,我们只能在类内部使用 setter。

        【讨论】:

        • 你的前三段有些不准确;即使在同一个文件中定义了合成变量(参见我的帖子),它仍然无法访问。其次,scope directives 不仅仅是提示;尝试访问@private ivar 会导致编译器错误。没错,使用运行时函数可以解决这个问题。
        • 你是对的,一个文件中可以有两个实现,然后在行为@private。不管怎样,第一个提示为什么不能访问是因为它不包含在内。
        【解决方案5】:

        您可以创建一个动态属性并向编译器指示它的实例化将在运行时进行。

        然后在您的子类中编写您自己的 getter 或合成属性。

        @interface BaseClass : NSObject

        @property (nonatomic, strong) NSString *ThisWillBeSynthesizedInRespectiveSubclasses;

        @结束

        @implementation 基类

        @dynamic ThisWillBeSynthesizedInRespectiveSubclasses;

        @结束

        在子类中

        @interface 子类:基类

        @结束

        @implementation 子类 @synthesize ThisWillBeSynthesizedInRespectiveSubclasses = _ThisWillBeSynthesizedInRespectiveSubclasses;

        @结束

        或者您编写自己的 setter / getter 方法。

        希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 2021-02-27
          • 2010-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-17
          • 1970-01-01
          • 2021-09-12
          相关资源
          最近更新 更多