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

hzx*_*zxu 31 inheritance visibility subclass objective-c declared-property

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

@interface MyClass : NSObject

@property (nonatomic, strong) MyPublicObject *publicObject;

- (void)publicMethod;

@end
Run Code Online (Sandbox Code Playgroud)

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

@interface MyClass()

@property (nonatomic, strong) MyPrivateObject *privateObject;

- (void) privateMethod;

@end
Run Code Online (Sandbox Code Playgroud)

并且我认为没有protected类型的私有但可以从子类访问.我想知道,除了公开宣布私有财产/方法之外,还有什么可以做到这一点吗?

Car*_*zey 34

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

@interface SuperClass ()

@property (nonatomic, strong) id someProperty;

@end

....


@interface SubClass ()

@property (nonatomic, strong) id someProperty;

@end

@implementation SubClass

@dynamic someProperty;

@end
Run Code Online (Sandbox Code Playgroud)

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

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

  • @abbood好吧,如果它们真的是私有的,那么子类不应该使用它们;)但是 - 我认为拥有SuperClass + Protected标头的替代方案是可行的 - 这正是Apple正式支持的(至少例如). (4认同)
  • 你不仅要再次宣布它......你也是第三次使用`@ dynamic`业务编写它...那是写一个变量3次而不是一次! (3认同)
  • 我简直不敢相信......你认真吗?所以你说我必须在每个子类中复制这些私有变量的声明??? 这太不方便了......必须有另一种方式 (2认同)

d4n*_*4n3 14

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

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

@interface MyClass ()
Run Code Online (Sandbox Code Playgroud)

在类扩展中,您可以声明属性,这将能够合成支持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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

ChildClass.h

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

#import "BaseClass.h"

@interface ChildClass : BaseClass

-(void)test;

@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

当您调用时#import,它基本上将.h文件复制粘贴到您导入它的位置.如果你有#ifdef,那么如果设置#define了那个名字,它将只包含里面的代码.

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


Ale*_*ith 8

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

私人,protected和public 可得到的,例如变量这样:

@interface MyClass : NSObject {
@private
  int varA;

@protected
  int varB;

@public
  int varC;
}

@end
Run Code Online (Sandbox Code Playgroud)

  • 这些是实例变量,objc没有类变量的概念. (8认同)