【问题标题】:Creating properties only visible to subclass in Objective-C在 Objective-C 中创建仅对子类可见的属性
【发布时间】:2013-10-19 16:51:53
【问题描述】:

我正在尝试创建一个抽象类并在子类中继承它的一些属性。如果我将属性保留在抽象类的头文件中,则所有属性都可以访问。问题是子类的实例也可以访问这些属性,这在我的情况下并不总是可取的。

例如,我的抽象类中有一个委托,它将按下按钮按下它的子类。我意识到这可能不是构建继承的最佳方式,因此欢迎提出其他建议。但是,我仍然想知道我的子类如何从它的超类继承一些属性,而不使所有这些属性在其实例中可用。提前致谢!

下面是一些示例代码:

@interface AbstractClass : UIView

@property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;

@end

…

@protocol ButtonDelegate

@required
- (void) buttonWasPressed;

@end

…

@interface SubClass() <ButtonDelegate>

- (id)init {
    self = [super init];
    if (self) {
        self.buttonDelegate = self;
    }
    return self;
}

-(void) buttonWasPressed {
    [self doSomething];
}

…

@implementation ViewController

- (void)viewDidLoad {
    SubClass *subClass = [[SubClass alloc] init];
    subClass.buttonDelegate = self; // THIS IS NOT DESIRABLE
}

【问题讨论】:

  • 看看 Apple 如何使用 UIGestureRecognizer 做到这一点。
  • 对不起,我应该对我所尝试的内容更彻底。我查看了 Apple 的实现(即私有属性的单独头文件),但这不是最佳解决方案,因为我的 Abstract 类中的属性对于子类正常运行至关重要。也感觉属性与类分离。
  • 问题是Objective-C不支持受保护的方法或属性。这就是 Apple 使用单独的头文件的原因。
  • 当你说“Objective-C 不支持受保护的方法或属性”时,我认为你一针见血。

标签: ios objective-c inheritance scope


【解决方案1】:

UIGestureRecognizer 那样做。

  1. 所有公共属性和方法都进入UIGestureRecognizer.h

  2. 所有受保护的属性和方法都进入UIGestureRecognizerSubclass.h。 仅在*.m-文件中导入它。切勿将其包含在任何公共标头中。

  3. 所有私有属性和方法都进入*.m-files。使用@interface ClassName ()

例如https://gist.github.com/hfossli/8041396

【讨论】:

  • 感谢您的回复。我看到了这个示例,但我想将相关属性保留在它们的类中,而不是拥有单独的文件。
  • 嗯,这不是您以前使用的语言。跨语言有不同的方法来实现相同的目标。这是其中之一。无论如何导入子类头有什么麻烦?
  • @hfossli 您是否像苹果一样使用 UIGestureRecognizerSubclass 的类别?似乎为这些“受保护的属性”提供存储的唯一方法是使用关联的对象。没有?
  • 或者也将属性添加到私有类别中的父类中。模糊。
  • @BobSpryn 我添加了一个更好解释的示例。
【解决方案2】:

我看到了一种可以解决您的问题的方法,但是,它非常粗鲁。使用 Antonio 的建议并使用该属性创建私有类别。正如您所提到的,它的范围仅限于 .m 文件。所以你可以把你的子类放到那个文件中。如果子类很大,这将很难阅读代码,但据我所知,这是您的唯一方法。

编辑:好吧,我有另一个解决方案。复制

@property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;

到你所有的子类。这会给你一个关于该属性的@synthesize 缺失的警告,但应该可以工作。如果不经常更改或添加子类,我更喜欢这个。

让我描述一下它是如何工作的。

我们在 Abstract 类中添加一个属性,它对所有人(甚至对子类)都是隐藏的:

// .m file

@interface Abstract ()

@property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;

@end


@implementation Abstract

@synthsize buttonDelegate;

@end;

但是由于 Objective-C 的运行时特性,我们仍然可以调用该属性,并且不会出现任何运行时错误,只有编译器警告。

为了消除该警告并添加自动完成功能,我们将不带 @synthsize 的属性添加到所有子类中:

@interface MySubclass : Abstract

@property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;

@end

这将告诉编译器某处存在这样的属性。还有一个关于没有@synthesize 的警告,但是如果你写类似的东西,Xcode 仍然可以自动完成

MySubclass *subclass = ...
subclass.butto...

【讨论】:

  • 如果我错了,请纠正我,但这不会只是使子类的属性而不是超类/抽象类的属性吗?我问是因为这听起来是一个有趣的解决方案。
  • 子类可以说是相当“固定”的。正如jano所建议的那样,在将ivar声明为超类中受保护的同时使用您的解决方案实际上接近受保护的属性。 Xcode 5 给出“自动合成属性 'buttonDelegate' 将使用合成实例变量 '_buttonDelegate',而不是现有实例变量 'buttonDelegate'”的警告。这编译没有错误。为了摆脱警告,我按照您的建议合成了该属性。理想的做法是在 AbstractClass 中将属性声明为受保护,但这不是一个糟糕的解决方法。
  • @Chris 不,这将作为抽象类的属性,因此子类仍然拥有它。查看我对问题的编辑
  • 感谢您的解释,这完全有道理。但是,我不希望在子类的实例中可以访问从子类中的超类继承的某些属性。在这种情况下 subclass.buttonDelegate 是不可取的。
【解决方案3】:

我的子类如何从它的超类继承一些属性 没有在其实例中提供所有这些属性

这有什么问题?

#import <Foundation/Foundation.h>

@interface Animal : NSObject
{
    @protected
    NSString *name; // default access. Only visible to subclasses.
}
@end

@implementation Animal
-(NSString*)description {
    return name;
}
@end

@interface Cow : Animal
@end

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        name   = @"cow";
    }
    return self;
}
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        NSLog(@"%@", cow); // prints the name through internal access
        // error accessing from the outside: NSLog(@"%@", cow.name);

        Animal *animal = [Animal new];
        // error accessing from the outside: NSLog(@"%@", animal.name);
    }
}

也许我误解了这个问题,你说

在 Objective-C 中创建仅对子类可见的属性

然后

问题是子类的实例也可以访问那些 属性

是哪一个?

【讨论】:

  • 问题是“在 Objective-C 中创建仅对子类可见的属性”,您清楚地回答了它。由于在stackoverflow上没有提到使用protected的帖子(至少我发现),使用它而不是使用Apple使用的方法有什么缺点吗?
  • 另外,有没有办法创建一个受保护的属性而不是 ivar?
  • ivars 是原子的(一次可以从一个线程访问)。这意味着由于锁,它们在多线程中慢了大约 4 倍,但由于直接访问,它们在单线程中更快。这个时间对于不经常访问来说是微不足道的。您不能同时使用 protected 和 property。你可以写 {@protected NSString *_name; } @property (nonatomic,strong) NSString *name;并且编译器将使用 _name 代替生成一个新变量,但它不会受到保护。 @protected 是括号内变量的默认值。 @public 具有与默认 @ 属性相同的属性。
  • 感谢您的回复。我不知道 ivars 是原子的,因此在多线程中慢了 4 倍。那么按照objective-c的标准,没有受保护的财产这样的东西吗?也许这就是为什么连 Apple 都会为受保护的属性使用变通方法。
  • 大多数代码在主线程中运行,这是串行的,所以没关系,但许多人认为设计错误使原子成为默认值。没有受保护的属性。如果你好奇,我在这里写了很多变量组合:stackoverflow.com/a/14906215/412916
【解决方案4】:

这是做不到的。在objective-c 中没有private 或protected。在 .m 文件“私有”接口中声明的内容仅对该类可见,而在任何子类中均不可见。如果您愿意,您也可以随时从外部使用您的“私有”属性/方法,尽管这样做是不好的做法。

【讨论】:

    【解决方案5】:

    我认为没有任何方法可以使用属性声明来实现这一点。

    属性要么对所有人可见(在 .h 文件中声明),要么对所有人不可见(在 .m 文件中使用类别声明)

    我猜一种方法是在 .h 文件类声明中声明公共/受保护变量:

    @interface AbstractClass : UIView {
    
       ...
    
       id<ButtonDelegate>buttonDelegate;
    
       ...
    }
    @end
    

    我不确定,但请尝试一下。

    【讨论】:

    • 声明私有变量不会影响任何属性的行为。
    • @Chuck:我建议声明一个私有变量而不是属性本身。
    【解决方案6】:

    在您的实现文件 (.m) 之上创建一个空类别:

    @interface AbstractClass()
    
    @property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
    
    @end
    

    这样,您的子类将继承并可以访问该属性,但不能访问其他外部类,因为它不在标头中。

    【讨论】:

    • 子类看不到它,不是吗?
    • 好吧,我自己也试过了,它是不可见的 :) 子类导入抽象类的标题,但这个类别在 .m 中。请参阅我的问题here,我遇到了类似的问题
    • 我尝试了这个解决方案,但它对我不起作用。这似乎是创建私有财产的最佳方式,但其范围有限。
    • @medvedNick,我查看了您的问题并看到了 bbum(一位备受尊敬的 Objective-C 开发人员)的答案。我想知道是否有任何方法可以在没有标记为 Private 的单独头文件的情况下完成此操作。
    • @AntonioMG,我又试了一次,子类没有看到在 .m 的接口 AbstractClass() 中声明的属性。
    猜你喜欢
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 2011-04-09
    • 1970-01-01
    相关资源
    最近更新 更多