Подтвердить что ты не робот

Выдать частный метод 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 для вещей, которые являются частными, но доступными из подклассов. Интересно, есть ли все равно для достижения этого, кроме публичного объявления частных свойств/методов?

4b9b3361

Ответ 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

Это потому, что нет даже реального различия между частным и общественным. Хотя компилятор может предупредить вас о том, что интерфейс не содержит определенный метод или переменную экземпляра, ваша программа все равно будет работать.