[英]Expose a private Objective-C method or property to subclasses
根據一些官方演講,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
類型。 我想知道,除了公開宣布私有財產/方法之外,還有什么方法可以實現?
解決此問題的一種方法是在子類的類擴展中重新聲明該屬性,然后添加@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”。 這樣,其他程序員將知道他們不應該導入文件。 但是,如果您不控制要從中繼承的代碼,那不是一個選擇。
這可以通過使用包含在基類和子類的實現文件中的類擴展名(而不是類別)來實現。
類擴展的定義類似於類別,但沒有類別名稱:
@interface MyClass ()
在類擴展中,您可以聲明屬性,這些屬性將能夠合成支持的ivars(XCode> 4.4的ivars的自動合成也可以在此處使用)。
在擴展類中,您可以覆蓋/優化屬性(將只讀更改為讀寫等),並添加對實現文件“可見”的屬性和方法(但請注意,這些屬性和方法並非真正私有,可以仍由選擇器調用)。
其他人建議為此使用單獨的頭文件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
,以便編譯器將包括受保護的類擴展名。
雖然其他答案正確,但我想補充一下...
私有,保護和公共可供實例變量這樣:
@interface MyClass : NSObject {
@private
int varA;
@protected
int varB;
@public
int varC;
}
@end
您唯一的選擇是在頭文件中將其聲明為public。 如果至少要保持某種方法的分離,則可以創建一個類別,並在其中保留所有受保護的方法和屬性,但是最后,所有內容仍將是公共的。
#import "MyClass.h"
@interface MyClass (Protected)
- (void) protectedMethods;
@end
只需使用您的類擴展名創建.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/6461536b99ef73a5c32a
我看到了使屬性可見的好答案,但是我看不到在任何這些答案中都非常清楚地公開解決的方法。 這是我使用類別成功將私有方法公開給子類的方法:
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
那是因為私人與公共之間甚至沒有真正的區別。 盡管編譯器可能會警告您某個接口缺少某些方法或實例變量,但您的程序仍然可以運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.