Можете ли вы создать методы класса в Objective-C, видимые только в вашей библиотеке? - PullRequest
0 голосов
/ 02 октября 2019

Я нашел этот вопрос StackOverflow о классах друзей в Objective-C , который был похож на проблему, которую я пытаюсь решить, но не совсемтакой же. Поэтому я хотел бы расширить их пример.

Предположим, я хочу иметь возможность передать Обезьяне несколько бананов, и он сохранит массив всех своих собственных бананов. Когда я говорю ему, чтобы поесть, он ест из своего массива. Затем я могу попросить его дать мне банан наугад:

Banana.h

@interface Banana : NSObject
- (BOOL)isPeeled;
@end

Banana.m

#import "Banana.h"

@implementation Banana
{
    @private
    BOOL peeled;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        peeled = NO;
    }
    return self;
}

- (void)peel // PRIVATE METHOD
{
    peeled = YES;
}

- (BOOL)isPeeled
{
    return peeled;
}
@end

Monkey.h

#import "Banana.h"

@interface Monkey : NSObject
- (void)give:(Banana *)aBanana;
- (void)eat;
- (Banana *)getBanana;
@end

Monkey.m

#import "Monkey.h"

@implementation Monkey
{
    @private
    NSMutableArray *bananas;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        bananas = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)give:(Banana *)aBanana
{
    [bananas addObject:aBanana];
}

- (void)eat
{
    BOOL hungry = YES;
    for (int i = 0; i < bananas.count; i++)
    {
        if (!banana.isPeeled)
        {
            hungry = NO;
            [banana peel];
            break;
        }
    }
    if (hungry)
    {
        NSLog(@"Monkey ANGRY!");
    }
}

- (Banana *)getBanana
{
    int r = arc4random_uniform(bananas.count);
    return [bananas objectAtIndex:r];
}
@end

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

ClientApp.m

#import "MonkeyLib/MonkeyLib.h"

@implementation SomeClass

- (void)someMethod
{
    Banana *oneBanana = [[Banana alloc] init];
    Monkey *oneMonkey = [[Monkey alloc] init];
    [oneMonkey give:oneBanana];
    [oneMonkey eat];
    Banana *twoBanana = [oneMonkey getBanana];
    if (twoBanana.isPeeled)
    {
        NSLog(@"No sad monkeys!");
    }
}

@end

Непосредственная проблема с кодом выше заключается в том, что моя библиотека не будеткомпилировать, потому что никакой интерфейс для класса Banana не определяет метод peel (используемый Monkey.m)

Вопрос StackOverflow, с которым я связался в начале этого вопроса, предлагает создать третий файл заголовка:

BananaPrivate.h

@interface Banana (PrivateMethods)
- (void)peel;
@end

Затем в Monkey.m я могу импортировать BananaPrivate.h и внезапно вызвать метод peel. Однако это не удается, потому что, когда я пытаюсь импортировать BananaPrivate.h, я создаю коллизию - я уже определил Banana, когда я импортировал Banana.h в Monkey.h

Я не могу удалить Banana.hиз Monkey.h, потому что один из методов в Monkey возвращает экземпляр типа Banana!

Я нашел причудливый обходной путь путем подкласса Banana, например, так:

InternalBanana.h

@interface InternalBanana : Banana
- (void)peel;
@end

Тогда в пределах Monkey.m я могу безопасно #import "InternalBanana.h". Однако это имеет две проблемы:

  1. Это работает только для бананов, инстанцированных моей Обезьяной (если пользователь моей библиотеки создает экземпляр банана и передает его моей Обезьяне, это будетбанан, а не InternalBanana)
  2. Мне нужно создать файл InternalBanana.m, но в этом файле я не могу определить peel. Попытка определить peel в InternalBanana.m приведет к ошибке, что peeled не определено, так как peeled является закрытой переменной суперкласса. Если я определю peel в InternalBanana.m и вызову будет [super peel], это также приведет к ошибке, поскольку Banana не определяет метод peel. Хотя этот работает , он заставляет XCode выдать предупреждение о том, что для peel нет определения метода (но он создается и работает, несмотря на предупреждение)

Что такое собственно решение для этого обезьяньего бизнеса?

...