Вопрос

Я хотел бы указать протокол Objective-C с необязательной рутиной. Когда процедура не реализована классом, соответствующим протоколу, я хотел бы использовать реализацию по умолчанию на его месте. Есть ли место в самом протоколе, где я могу определить эту реализацию по умолчанию? Если нет, то какова лучшая практика для уменьшения копирования и вставки этой реализации по умолчанию повсюду?

Это было полезно?

Решение

Протоколы Objective-C не имеют возможности для реализаций по умолчанию. Это чисто коллекции деклараций методов, которые могут быть реализованы другими классами. Стандартная практика в Objective -C состоит в том, чтобы проверить объект во время выполнения, чтобы увидеть, отвечает ли он на заданный селектор, прежде чем называть этот метод на нем, используя -[nsobject responsestoselector:]. Если E -объект не отвечает на заданный селектор, метод не вызван.

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

Другой подход будет заключаться в том, чтобы сделать метод потребоваться в протоколе и предоставить реализации по умолчанию в суперклассах любых классов, в которых вы, возможно, не захотите предоставлять конкретную реализацию.

Вероятно, есть и другие варианты, но вообще говоря, в Objective-C нет конкретной стандартной практики, за исключением того, что, возможно, просто не называть данного метода, если он не был реализован объектом, согласно моим первым абзацам, выше Анкет

Другие советы

Не существует стандартного способа сделать это, поскольку протоколы не должны определять каких -либо реализаций.

Поскольку Objective-C поставляется с аккуратным временем выполнения, вы, конечно, можете добавить такое поведение, если вы действительно думаете, что вам нужно сделать это таким образом (и нет возможности, достигая его с наследством).

Скажем, вы объявили MyProtocol, затем просто добавьте интерфейс с тем же именем в файле .H в соответствии с объявлением протокола:

@interface MyProtocol : NSObject <MyProtocol>

+ (void)addDefaultImplementationForClass:(Class)conformingClass;

@end

И создать соответствующий файл реализации (с помощью Maobjcruntime Для читаемости здесь, но стандартные функции времени выполнения не будут намного больше кода):

@implementation MyProtocol

+ (void)addDefaultImplementationForClass:(Class)conformingClass {
  RTProtocol *protocol = [RTProtocol protocolWithName:@"MyProtocol"];
  // get all optional instance methods
  NSArray *optionalMethods = [protocol methodsRequired:NO instance:YES];
  for (RTMethod *method in optionalMethods) {
    if (![conformingClass rt_methodForSelector:[method selector]]) {
      RTMethod *myMethod = [self rt_methodForSelector:[method selector]];
      // add the default implementation from this class
      [conformingClass rt_addMethod:myMethod];
    }
  }
}

- (void)someOptionalProtocolMethod {
  // default implementation
  // will be added to any class that calls addDefault...: on itself
}

Тогда вам просто нужно позвонить

[MyProtocol addDefaultImplementationForClass:[self class]];

В инициализаторе вашего класса, соответствующего протоколу, и все методы по умолчанию будут добавлены.

По -настоящему увлекательный способ - использовать время выполнения. В начале, очень рано в выполнении программы, сделайте следующее:

  1. Перечислять все классы, найти классы, которые реализуют протокол
  2. Проверьте, реализует ли класс метод
  3. Если нет, добавьте в класс реализация по умолчанию

Это может быть достигнуто без особых проблем.

Как упоминает Райан, для протоколов нет реализаций по умолчанию, еще одна вариант реализации в SuperClass состоит в том, чтобы внедрить класс «обработчика», который можно содержать в любом классе, который хочет предоставить реализацию по умолчанию, соответствующий метод вызовы. реализация по умолчанию.

В итоге я создал макрос, у которого есть реализация метода по умолчанию.

Я определил его в заголовке протокола, а затем это всего лишь однострочный в каждой реализации.

Таким образом, мне не нужно менять реализацию в несколько мест, и это сделано во время компиляции, поэтому магия времени выполнения не требуется.

Я согласен с «WM» очень хорошим решением - поместить все реализации по умолчанию в интерфейс (с тем же именем, что и протокол). В методе «+инициализировать» любого подкласса он может просто скопировать любые невыполненные методы из интерфейса по умолчанию.

Следующие вспомогательные функции сработали для меня

#import <objc/runtime.h>

// Get the type string of a method, such as "v@:".
// Caller must allocate sufficent space. Result is null terminated.
void getMethodTypes(Method method, char*result, int maxResultLen)
{
    method_getReturnType(method, result, maxResultLen - 1);
    int na = method_getNumberOfArguments(method);
    for (int i = 0; i < na; ++i)
    {
        unsigned long x = strlen(result);
        method_getArgumentType(method, i, result + x, maxResultLen - 1 - x);
    }
}

// This copies all the instance methods from one class to another
// that are not already defined in the destination class.
void copyMissingMethods(Class fromClass, Class toClass)
{
    // This gets the INSTANCE methods only
    unsigned int numMethods;
    Method* methodList = class_copyMethodList(fromClass, &numMethods);
    for (int i = 0; i < numMethods; ++i)
    {
        Method method = methodList[i];
        SEL selector = method_getName(method);
        char methodTypes[50];
        getMethodTypes(method, methodTypes, sizeof methodTypes);

        if (![toClass respondsToSelector:selector])
        {
            IMP methodImplementation = class_getMethodImplementation(fromClass, selector);
            class_addMethod(toClass, selector, methodImplementation, methodTypes);
        }
    }
    free(methodList);
}

Затем вы называете его в своем классе инициализатора, например ...

@interface Foobar : NSObject<MyProtocol>  
@end

@implementation Foobar
+(void)initialize
{
    // Copy methods from the default
    copyMissingMethods([MyProtocol class], self);
}
@end

Xcode даст вам предупреждения о пропущенных методах Foobar, но вы можете игнорировать их.

Этот метод только копирует методы, а не ivars. Если методы получают доступ к членам данных, которые не существуют, вы можете получить странные ошибки. Вы должны убедиться, что данные совместимы с кодом. Как будто вы сделали переосмысление из Foobar до MyProtocol.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top