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




inheritance subclass (6)

根据官方的一些话题,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类型。 我想知道,除了公开宣布私有财产/方法之外,还有什么可以做到这一点吗?


Answer #1

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

#import "MyClass.h"

@interface MyClass (Protected)

- (void) protectedMethods;

@end

Answer #2

我看到了使属性可见的良好答案,但我没有看到在任何这些答案中非常清楚地揭示了所解决的方法。 以下是我使用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

Answer #3

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

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

@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文件中,您没有设置定义,因此导入此.h的任何类都不会看到受保护的类扩展。 在基类和子类.m文件中,在使用#import之前使用#define ,以便编译器包含受保护的类扩展。


Answer #4

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


Answer #5

解决此问题的一种方法是在子类的类扩展中重新声明属性,然后添加@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”。 这样,其他程序员就会知道他们不应该导入文件。 但是,如果你不控制你继承的代码,那不是一个选择。


Answer #6

只需使用类扩展名创建一个.h文件。 将其导入.m文件。 顺便说一下,这是一个很好的方法来测试私有成员而不破坏封装(我不是说你应该测试私有方法:))。

// MyClassProtectedMembers.h
@interface MyClass()

@property (nonatomic, strong) MyPrivateObject *privateObject;
- (void) privateMethod;
@end

/////////////////

#import "MyClassProtectedMembers.h"

@implementation MyClass
// implement privateMethod here and any setters or getters with computed values
@end

以下是这个想法的要点: https://gist.github.com/philosopherdog/6461536b99ef73a5c32ahttps://gist.github.com/philosopherdog/6461536b99ef73a5c32a





declared-property