Выдать частный метод Objective-C или свойство для подклассов
Согласно официальному сообщению, класс в 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
для вещей, которые являются частными, но доступными из подклассов. Интересно, есть ли все равно для достижения этого, кроме публичного объявления частных свойств/методов?
Ответы
Ответ 1
Один из способов решения этой проблемы - повторно объявить свойство в расширении класса подкласса, а затем добавить инструкцию @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". Таким образом, другие программисты будут знать, что они не должны импортировать файл. Но, если вы не контролируете код, который вы наследуете, это не вариант.
Ответ 2
Это возможно с помощью расширения класса (не категории), которое вы включаете в файлы реализации как базового класса, так и подклассов.
Расширение класса определено аналогично категории, но без названия категории:
@interface MyClass ()
В расширении класса вы можете объявить свойства, которые смогут синтезировать поддерживающие ivars (XCode > 4.4, автоматический синтез иваров также работает здесь).
В классе расширения вы можете переопределить/уточнить свойства (изменить 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 файла вы используете #define
перед использованием #import
, чтобы компилятор включил расширение защищенного класса.
Ответ 3
В то время как другие ответы верны, я хотел бы добавить...
Частные, защищенные и общедоступные доступны, например, переменные:
@interface MyClass : NSObject {
@private
int varA;
@protected
int varB;
@public
int varC;
}
@end
Ответ 4
Ваш единственный выбор - объявить его общедоступным в файле заголовка. Если вы хотите хотя бы сохранить разделение методов, вы можете создать категорию и иметь все свои защищенные методы и атрибуты, но в конце все будет по-прежнему публично.
#import "MyClass.h"
@interface MyClass (Protected)
- (void) protectedMethods;
@end
Ответ 5
Просто создайте файл .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
Ответ 6
Я вижу хорошие ответы для того, чтобы сделать видимыми свойства, но я не вижу разоблачения методов, которые очень четко рассматриваются в любом из этих ответов. Вот как я успешно открыл частные методы для подкласса, используя Категория:
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
Ответ 7
Это потому, что нет даже реального различия между частным и общественным. Хотя компилятор может предупредить вас о том, что интерфейс не содержит определенный метод или переменную экземпляра, ваша программа все равно будет работать.