Быстрый интерфейсный шаблон в Objective C

Я - новичок в Цели-c, и я хотел бы реализовать быстрый интерфейсный шаблон в своем классе OC. Вот мой обновленный и упрощенный случай из моего проекта:

// .h file
@interface MyLogger : NSObject { 
 ... 
}
- (MyLogger*) indent:(BOOL)indent;
- (MyLogger*) debug:(NSString*)message, ...;
- (id) warning:(NSString*)message, ...;
....
@end

// .m file
@implement MyLogger {
- (MyLogger*) indent:(BOOL)indent {
   // some codes to set indent or unindent
   return self; // I think it should return [self autorelease];
}
- (MyLogger*) debug:(NSString*)message, ... {
    // build message and log the message
    return [self autorelease];
}
- (id) warning:(NSString*)message, ... {
  // similar as above, but log a warning message
  return self;
}

//. usage in another .m
  -(id) initAnotherClass {
    if (self = [supper init]) {
      // ...
      // instance was defined as MyLogger in .h as class var
      instance = [[[MyLogger alloc] initWithContext:@"AnotherClassName"] retain];
     //...
     }
   return self;
  }

  -(void)method1 {
   [[instance debug:@"method1"] indent:YES];
   ...
   [instance warning:@"some debug message with obj: %@", var];
   ...
   [[instance indent:NO] debug:@"method1 DONE"];
 }

 // in my Xcode output I'll see debug output like
 [date time] [app id] [DEBUG] AnotherClassName - method1
 [date time] [app id] [WARNING]   AnotherClassName - some debug message with obj: ...
 [date time] [app id] [DEBUG] AnotherClassName - method1 DONE

Здесь в indent, Я возвращаюсь сам, в то время как в debug: Я возвращаюсь [self autorelease]. Это хорошо работает, если я только возвращаюсь self как в debug. Однако я думаю, что должен всегда возвращаться таким же образом, когда я выполнил debug: с точки зрения управления памятью OC. Какие-либо предложения?

Обновленный: Я добавил другой метод warning с типом возврата идентификатора. Я должен возвратиться сам как идентификационный тип или мой тип класса в OC? Это кажется и хорошо работает и нет никакой ошибки компиляции или предупреждения. Я имею, кажутся, что классы платформы Какао возвращают идентификатор. Например, вот некоторые методы в NSString.h

+ (id)string;
+ (id)stringWithString:(NSString *)string;

Кажется, что Какао имеет некоторый шаблон FI как методы. Должен быть идентификационный тип лучше, чем сам класс?

Обновление: как предложение Pat Wallace, я на самом деле использую этот шаблон в проекте iPhone.

9
задан Community 23 May 2017 в 10:27
поделиться

4 ответа

Несколько примечаний:

  1. Когда вы возвращаете существующий объект из метода, если вы все еще «заботитесь» об этом объекте, вы не освобождаете его автоматически, вы просто возвращаете его. В этом случае, поскольку вы «сохраняете» свой собственный объект даже после того, как вызывающий объект получает ссылку на него, не отправляйте ему сообщение autorelease . Не думайте о шаблоне как о «возврате автоматически выпущенного объекта»; вы делаете это только тогда, когда создаете объект внутри метода и хотите вернуть его, не сохраняя ссылку самостоятельно. Если вызывающий хочет сохранить полученную ссылку, он может сохранить ее.

  2. self - это своего рода ссылка особого типа, и очень редко можно отправить self какие-либо сообщения управления памятью, за возможным исключением внутри init метод.

  3. Хотя вы, безусловно, можете создать Fluent-шаблон цепочки сообщений, как вы пытаетесь это сделать, просто обратите внимание, что это не общий / идиоматический Objective-C, и ваш код может плохо сочетаться с другим кодом и может сбивать с толку другие, кто это читал. Просто к вашему сведению.

10
ответ дан 4 December 2019 в 06:03
поделиться

О, воспоминания ....

Waayyy еще до того, как префикс NS * использовался повсюду, для всех методов было стандартным возвращать self; что в наши дни иметь тип возвращаемого значения (void) .

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

На практике это отстой. И под «на практике» я имею в виду «после поддержки нескольких сотен тысяч строк сильно связанных методов, вызывающих код Objective-C, я пришел к выводу, что цепочка методов была гигантской головной болью и, в конечном итоге, избегать".

В частности, я говорю о:

 [[[[[[self foo] bar] baz] bob] fred] eatMe];

А не:

x = [self foo];
x = [x bar];
x = [x baz];
x = [x bob];
x = [x fred];
x = [x eatMe];

(Добавлен x = как исходное, но не то же самое выражение.)

Первое - это полностью связанная форма, а последняя представляет собой шаблон кода, который вы видите сегодня, и оба они появляются в различных описаниях FLUENT.

Когда был разработан OpenStep API - то, что вы сейчас называете Какао , - дизайнеры пришли к тем же выводам и, таким образом, к соглашению об использовании по умолчанию (недействительно) тип возвращаемого значения был принят во всех фреймворках.

Есть ряд проблем с шаблоном (некоторые из которых являются прямым следствием Objective-C, некоторые из них связаны с базовыми инструментами). Отчасти это, конечно, мнение. Отнеситесь к этому с недоверием:

  1. Отладка - это прямая боль; вы не можете установить точку останова для произвольного вызова метода с субцепями в однострочной форме fluent-y. Переход «вверх» или «вниз» с помощью выражения метода с плавной цепочкой может сбивать с толку; какое подвыражение это было снова?

  2. Неожиданные nil еще хуже. В приведенном выше примере, скажем, -baz неожиданно возвращает nil . Чтобы выяснить это, вам нужно будет установить точку останова для всех последующих методов (по крайней мере), или вам придется разбить ее, а затем проверить результаты подвыражений, или перепрыгнуть через другие обручи, чтобы понять это. из.

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

  4. Лично я считаю, что методы, связанные с цепочкой, труднее читать. Он больше не читается как серия шагов (что есть на самом деле), а читается как предложение.Это звучит аккуратно, но на практике это больше похоже на серию выражений, серию шагов, и трактовка этого как такового часто бывает более интуитивной.

  5. Он забирает очень ценный индикатор из вашего API. Метод, который возвращает (void) , очень явно говорит: «Я делаю что-то с аргументами, тогда все готово». Возвращаемое значение - имейте в виду, что вам часто придется объявлять возвращаемый тип как (id) , если задействовано подклассификация (ObjC вообще не выполняет ковариантность) - говорит: " Эй, чувак, я кое-что сделал, вот твой результат, разберись с ним ».

  6. Наличие непустых возвращаемых типов сделало распределенные объекты значительно менее эффективными. Непустой возвращаемый тип должен быть проксирован обратно по сети. Возврат (void) не требует такого прокси, и (oneway void) может быть отправлен по сети и выполнен асинхронно с локальной стороны, еще быстрее.


В любом случае, отвечу на ваш исходный вопрос: нет, вы почти никогда не вернете [[self keep] autorelease]; из такого контекста метода FLUENT-y. Как говорили другие, self является своего рода особенным, и тот факт, что вы даже выполняете метод, означает, что self будет существовать по крайней мере до тех пор, пока выполняется выполнение метода (потоки будут треклятый).

36
ответ дан 4 December 2019 в 06:03
поделиться

Я задал этот вопрос с тремя намерениями: проблема с памятью (автозапуск?), Использование self и лучший метод использования шаблона Fluent Interface в ObjC. Спасибо за прекрасные ответы и вдумчивый анализ. Однако я не уверен. Я не согласен вообще избегать FI в ObjC.

Я думаю, что на самом деле это проблема дизайна и архитектуры. Если вы можете создать класс с хорошо структурированным шаблоном FI, вы увидите его красоту и удобство использования, а также простоту обслуживания.Хотя я новичок в ObjC и у меня не так много воспоминаний или опыта, я действительно вижу много хороших практик FI и их широкое использование во многих проектах с открытым исходным кодом, языках и фреймворках. Вот некоторые из моих аргументов:

  1. Отладка вообще не проблема. Я не большой поклонник отладчика времени компиляции или отладки в Xcode, в случае ObjC. Я был бы потерян, если бы я делал несколько звонков и выходил из них. Я просто не могу вспомнить или отследить все состояния переменных или экземпляров. Кроме того, отладка во время компиляции сильно отличается от времени выполнения или в некоторых случаях просто невозможна. Например, события и потоки сводят вас с ума. Я думаю, что отладка во время компиляции просто тратит время и энергию, и это мое последнее средство. Если вы чувствуете боль при отладке FI, не вините FI, расслабьтесь и спросите себя, правильно ли вы делаете это? Вместо этого я бы использовал такие инструменты, как NSLog (), некоторые другие инструменты (например, мой собственный класс-оболочку MyLogger , еще не законченный, но скоро) или модульные тесты, чтобы помочь мне.

  2. Unexpect nil - очень интересная особенность ObjeC (не вызывает исключений). Если бы это случилось со мной, я бы не стал винить ФИ. У меня хороший дизайн, подходит ли FI для случая, только мои неосторожные или тупые баги в моих кодах? Я не полностью доверяю своим кодам, если не могу доказать это своими модульными тестами. Другими словами, я бы тратил время и силы на написание модульных тестов, охватывающих все случаи и логический поток. Делая это, я надежно охраняю не только текущие коды, но и будущие работы по обслуживанию или обновлениям.

  3. Хороший дизайн и практика FI значительно упрощают рефакторинг. FI использовался во многих случаях, таких как подготовка сериалов к финальному действию, настройка, создание экземпляров и многое другое. Например, если у меня есть фрагменты кода для настройки чего-либо для подготовки с некоторыми временными переменными для действия, конкретной конфигурации или создания экземпляра результата, я бы подумал о том, чтобы инкапсулировать эти фрагменты в класс API FI с легкостью: понимайте методы как объединяемые в цепочки элементы. Класс предоставляет плавную и гибкую цепочку для выражения логики. Это не только удобно для чтения, но и для гибкого повторного использования и обслуживания.

  4. FI - это связанный поток, один вызов сразу за другим. Это предотвращает любые случайные вставки или удаления. Просто представьте, что кто-то другой пришел к моим кодам и не полностью понимает подготовку и логический поток кодов фрагментов, случайные изменения могут сломать приложение. На первый взгляд это может выглядеть некрасиво , а может людям это с первого взгляда не понравится. Тем не менее, вы увидите преимущества и веские причины для хорошо спроектированного API FI, если захотите изучить, по возможности увидеть, что внутри класса, а также способы использования API. Я думаю, некрасивый скрывает субъективный взгляд и отказывается учиться.

  5. FI не является структурой фиксированного рабочего потока, как это делают многие фрагменты кода. FI на самом деле предоставляет гибкий способ связать небольшие и четко определенные действия или настройки вместе, чтобы охватить различные случаи. В случае фрагментов, которые трудно понять и / или поддерживать, FI может быть решением для преобразования кодов в легко читаемую логическую цепочку.

  6. Не злоупотребляйте ФИ. Это наиболее частая причина создания уродливых кодов и трудных для понимания кодов. Если FI не подходит, не используйте его. FI не является идеальным решением, но он предоставляет шаблон для инкапсуляции промежуточных приготовлений и раскрытия гибкой логической схемы. Сделать хорошо спроектированный фреймворк или API-интерфейс непросто.Чтобы изучить его использование, нужны усилия. Вы взвешиваете плюсы и минусы.

0
ответ дан 4 December 2019 в 06:03
поделиться

Если у вас есть возможность переключиться на Objective-C 2 для вашего приложения, которое предлагает полную сборку мусора , вам не нужно беспокоиться о вызове функции сохранения / выпуска. / autorelease. (Объекты по-прежнему отвечают на эти сообщения, они просто ничего не делают).

Затем вы можете вернуть себя из своих беглых методов, и они будут очищены автоматически.

Приложения Objective-C 2 будут работать на Leopard (10.5) или новее.

0
ответ дан 4 December 2019 в 06:03
поделиться