Многократное наследование для IOS
Я хочу создать класс, который может наследовать из двух пользовательских классов.
У вас есть идея сделать это, пожалуйста?
См. Ниже мой пример:
первый класс:
@interface UIZoomableView : UIView
{
UITapGestureRecognizer *_tapGestureRecognizer;
}
и реализация:
- (void)onDoubleTap:(UITapGestureRecognizer *)sender
{
CGSize newSize;
CGPoint centerPoint = self.center;
if ([self isSmall])
{
newSize = [self bigSize];
}
else
{
newSize = [self smallSize];
}
[UIView animateWithDuration:0.3 animations:^{
self.size = newSize;
self.center = centerPoint;
}];
}
Второй класс:
@interface UIDraggableView : UIView
UIPanGestureRecognizer *_panGestureRecognizer;
@end
реализация:
- (void)handlePan:(UIPanGestureRecognizer*)sender
{
..
}
Я хочу создать настраиваемое представление, которое можно масштабировать и перетаскивать.
У вас есть идея сделать это, пожалуйста? (без кода копирования..)
Я думаю что-то вроде протоколов, но мне нужно значение по умолчанию для базовых классов?
Как я могу реализовать это с помощью протокола или что-то вроде протоколов.
Спасибо за любой ответ!
Ответы
Ответ 1
Objective-C не поддерживает множественное наследование. Вы можете использовать протокол, состав и пересылку сообщений для достижения того же результата.
Протокол определяет набор методов, которые должен реализовывать объект (возможно также иметь необязательные методы). Композиция - это в основном метод включения ссылки на другой объект и вызов этого объекта, когда требуется его функциональность. Пересылка сообщений - это механизм, который позволяет объектам передавать сообщения на другие объекты, например объект, который включен в состав композиции.
Справочник Apple:
Итак, в вашем случае композиция может быть решением, ниже приведен пример кода
@interface ClassA : NSObject {
}
-(void)methodA;
@end
@interface ClassB : NSObject {
}
-(void)methodB;
@end
@interface MyClass : NSObject {
ClassA *a;
ClassB *b;
}
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB;
-(void)methodA;
-(void)methodB;
@end
@implementation MyClass
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB {
a = anA ;
b = aB ;
}
-(void)methodA {
[a methodA] ;
}
-(void)methodB {
[b methodB] ;
}
@end
Если вы не хотите внедрять все методы из ClassA и ClassB в MyClass, вы можете использовать пересылку сообщений в MyClass для обработки всех вызовов метода. Ниже работает нормально, пока ClassA и ClassB не имеют общих методов.
@implementation MyClass
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB {
a = anA ;
b = aB ;
}
//This method will be called, when MyClass can not handle the method itself
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([a respondsToSelector:[anInvocation selector]])
[a invokeWithTarget:someOtherObject];
else if ([b respondsToSelector:[anInvocation selector]])
[b invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
@end
Ответ 2
Самое близкое, что вы можете получить к множественному наследованию в Objective C, - это категории. Это механизм добавления дополнительных методов в уже существующий класс.
Обратите внимание, что это имеет некоторые важные ограничения:
- Вы не можете добавлять свойства или ivars с помощью категории, хотя вы можете использовать связанные объекты, чтобы получить аналогичный эффект;
- Компилятор не скажет вам, есть ли у вас методы с тем же именем, которые объявлены в классе и категории, или в двух категориях, поэтому вы должны быть осторожны, чтобы избежать столкновения имен;
- Это не будет выглядеть как правильный класс (поскольку Objective C не имеет множественного наследования), поэтому у вас не будет чего-то в вашем коде с именем
ScrollableZoomableView
, который наследует от ScrollableView
и ZoomableView
. Это невозможно в Objective C (в отличие от С++, например).
- При связывании файлов с категориями вам нужен флаг
-ObjC
, иначе при запуске кода вы получите ошибки unrecognized selector
;
- Вы не можете получить свой код во время
-init
или +initialize
, потому что они относятся к базовому классу. Вы должны явно инициализировать свои свойства. Вы все равно можете использовать +load
, хотя:
- Вы также не можете перехватывать dealloc, поэтому вам может потребоваться также явно исключить регистрацию ваших слушателей.
Вы хотите что-то вроде этого:
@interface UIView (Zoomable)
@property (nonatomic) UITapGestureRecognizer * my_tapGestureRecognizer;
@end
@implementation UIView (Zoomable)
-(void)my_enableZooming() {
self.my_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(my_onDoubleTap:)];
self.my_tapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:self.my_tapGestureRecognizer];
}
-(void)my_disableZooming() {
[self removeGestureRecognizer:self.my_tapGestureRecognizer];
self.my_tapGestureRecognizer = nil;
}
-(void)my_onDoubleTap:(UITapGestureRecognizer *)sender {
...
}
-(UITapGestureRecognizer)my_tapGestureRecognizer {
return objc_getAssociatedObject(self, @selector(my_tapGestureRecognizer));
}
-(void)setMy_tapGestureRecognizer:(UITapGestureRecognizer)value {
objc_setAssociatedObject(self, @selector(my_tapGestureRecognizer), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@interface UIView (Draggable)
@property (nonatomic) UIPanGestureRecognizer * my_panGestureRecognizer;
@end
@implementation UIView (Draggable)
-(void)my_enableDragging() {
self.my_panGestureRecognizer = ...;
}
-(void)my_disableDragging() {
...
}
-(void)my_handlePan:(UIPanGestureRecognizer*)sender {
...
}
-(UIPanGestureRecognizer)my_panGestureRecognizer {
return objc_getAssociatedObject(self, @selector(my_panGestureRecognizer));
}
-(void)setMy_panGestureRecognizer:(UIPanGestureRecognizer)value {
objc_setAssociatedObject(self, @selector(my_panGestureRecognizer), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
Ответ 3
Вы также можете посмотреть эту статью в objc.io о поведении. Вы можете создавать перетаскиваемые и масштабируемые типы поведения, а затем добавлять их во все виды, которые вы хотите.
Здесь статья.