将私有Objective-C方法或属性公开给子类

时间:2012-09-28 04:34:23

标签: objective-c inheritance subclass visibility declared-property

根据官方的一些话题,Objective-C中的一个类只应在其标题中公开公共方法和属性:

@interface MyClass : NSObject

@property (nonatomic, strong) MyPublicObject *publicObject;

- (void)publicMethod;

@end

和私有方法/属性应保存在.m文件的类扩展中:

@interface MyClass()

@property (nonatomic, strong) MyPrivateObject *privateObject;

- (void) privateMethod;

@end

我认为私有但可从子类访问的内容不存在protected类型。我想知道,除了公开宣布私人财产/方法之外,还有什么可以做到这一点吗?

6 个答案:

答案 0 :(得分:34)

解决此问题的一种方法是在子类的类扩展中重新声明属性,然后添加@dynamic语句,以便编译器不会创建该属性的重写实现。如下所示:

@interface SuperClass ()

@property (nonatomic, strong) id someProperty;

@end

....


@interface SubClass ()

@property (nonatomic, strong) id someProperty;

@end

@implementation SubClass

@dynamic someProperty;

@end

这显然不太理想,因为它复制了一个私下可见的声明。但是在某些情况下它非常方便和有用,所以我会说逐案评估这种复制所涉及的危险与公开界面中的属性暴露。

Apple在UIGestureRecognizer中使用的替代方法是将属性声明为明确命名为“private”或“protected”的单独类别头文件,例如: “SomeClass的+ Protected.h”。这样,其他程序员就会知道他们不应该导入文件。但是,如果你不控制你继承的代码,那不是一个选择。

答案 1 :(得分:14)

通过使用包含在基类和子类的实现文件中的类扩展(非类别),可以实现这一点。

类扩展的定义类似于类别,但没有类别名称:

@interface MyClass ()

在类扩展中,您可以声明属性,这将能够合成支持ivars(XCode> 4.4自动合成ivars也可以在这里工作)。

在扩展类中,您可以覆盖/优化属性(将readonly更改为readwrite等),并添加对实现文件“可见”的属性和方法(但请注意,属性和方法不是真的私有,仍然可以被选择器调用。

其他人建议使用单独的头文件MyClass_protected.h,但这也可以使用#ifdef在主头文件中完成,如下所示:

示例:

BaseClass.h

@interface BaseClass : NSObject

// foo is readonly for consumers of the class
@property (nonatomic, readonly) NSString *foo;

@end


#ifdef BaseClass_protected

// this is the class extension, where you define 
// the "protected" properties and methods of the class

@interface BaseClass ()

// foo is now readwrite
@property (nonatomic, readwrite) NSString *foo;

// bar is visible to implementation of subclasses
@property (nonatomic, readwrite) int bar;

-(void)baz;

@end

#endif

BaseClass.m

// this will import BaseClass.h
// with BaseClass_protected defined,
// so it will also get the protected class extension

#define BaseClass_protected
#import "BaseClass.h"

@implementation BaseClass

-(void)baz {
    self.foo = @"test";
    self.bar = 123;
}

@end

ChildClass.h

// this will import BaseClass.h without the class extension

#import "BaseClass.h"

@interface ChildClass : BaseClass

-(void)test;

@end

ChildClass.m

// this will implicitly import BaseClass.h from ChildClass.h,
// with BaseClass_protected defined,
// so it will also get the protected class extension

#define BaseClass_protected 
#import "ChildClass.h"

@implementation ChildClass

-(void)test {
    self.foo = @"test";
    self.bar = 123;

    [self baz];
}

@end

当您致电#import时,它基本上会将.h文件复制粘贴到您导入的位置。 如果您有#ifdef,则只有在设置了具有该名称的#define时才会包含代码。

在你的.h文件中,你没有设置define,所以导入这个.h的任何类都不会看到受保护的类扩展。 在基类和子类.m文件中,在使用#define之前使用#import,以便编译器包含受保护的类扩展。

答案 2 :(得分:8)

虽然其他答案都是正确的,但我想补充一下......

私有,受保护和公共 可用,例如变量

@interface MyClass : NSObject {
@private
  int varA;

@protected
  int varB;

@public
  int varC;
}

@end

答案 3 :(得分:1)

您唯一的选择是在头文件中将其声明为public。如果你想至少保持一些方法分离,你可以创建一个类别,并在那里拥有所有受保护的方法和属性,但最终一切都将是公开的。

#import "MyClass.h"

@interface MyClass (Protected)

- (void) protectedMethods;

@end

答案 4 :(得分:1)

我看到了使属性可见的良好答案,但我没有看到在任何这些答案中非常清楚地揭示所述方法。以下是我使用Category:

成功将私有方法暴露给子类的方法

SomeSuperClass.m:

@implementation SomeSuperClass

-(void)somePrivateMethod:(NSString*)someArgument {
    ...
}

SomeChildClass.h

@interface SomeChildClass : SomeSuperClass

SomeChildClass.m

@interface SomeSuperClass (exposePrivateMethod)
-(void)somePrivateMethod:(NSString*)someArgument;
@end

@implementation SomeChildClass

-(void)doSomething {
    [super somePrivateMethod:@"argument"];
}

@end

答案 5 :(得分:0)

那是因为私人和公共之间甚至没有真正的区别。虽然编译器可能会警告您缺少某个方法或实例变量的接口,但您的程序仍然有效。