Я - новичок в Цели-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.
Несколько примечаний:
Когда вы возвращаете существующий объект из метода, если вы все еще «заботитесь» об этом объекте, вы не освобождаете его автоматически, вы просто возвращаете его. В этом случае, поскольку вы «сохраняете» свой собственный объект даже после того, как вызывающий объект получает ссылку на него, не отправляйте ему сообщение autorelease
. Не думайте о шаблоне как о «возврате автоматически выпущенного объекта»; вы делаете это только тогда, когда создаете объект внутри метода и хотите вернуть его, не сохраняя ссылку самостоятельно. Если вызывающий хочет сохранить полученную ссылку, он может сохранить ее.
self
- это своего рода ссылка особого типа, и очень редко можно отправить self
какие-либо сообщения управления памятью, за возможным исключением внутри init
метод.
Хотя вы, безусловно, можете создать Fluent-шаблон цепочки сообщений, как вы пытаетесь это сделать, просто обратите внимание, что это не общий / идиоматический Objective-C, и ваш код может плохо сочетаться с другим кодом и может сбивать с толку другие, кто это читал. Просто к вашему сведению.
О, воспоминания ....
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, некоторые из них связаны с базовыми инструментами). Отчасти это, конечно, мнение. Отнеситесь к этому с недоверием:
Отладка - это прямая боль; вы не можете установить точку останова для произвольного вызова метода с субцепями в однострочной форме fluent-y. Переход «вверх» или «вниз» с помощью выражения метода с плавной цепочкой может сбивать с толку; какое подвыражение это было снова?
Неожиданные nil
еще хуже. В приведенном выше примере, скажем, -baz
неожиданно возвращает nil
. Чтобы выяснить это, вам нужно будет установить точку останова для всех последующих методов (по крайней мере), или вам придется разбить ее, а затем проверить результаты подвыражений, или перепрыгнуть через другие обручи, чтобы понять это. из.
Это делает рефакторинг кода более утомительным. Если вы обнаружите, что вам нужно вставить новое подвыражение, проверить значение в середине или иным образом испортить выражение, вам нужно сначала разбить его. Со второй формой справиться намного проще.
Лично я считаю, что методы, связанные с цепочкой, труднее читать. Он больше не читается как серия шагов (что есть на самом деле), а читается как предложение.Это звучит аккуратно, но на практике это больше похоже на серию выражений, серию шагов, и трактовка этого как такового часто бывает более интуитивной.
Он забирает очень ценный индикатор из вашего API. Метод, который возвращает (void)
, очень явно говорит: «Я делаю что-то с аргументами, тогда все готово». Возвращаемое значение - имейте в виду, что вам часто придется объявлять возвращаемый тип как (id)
, если задействовано подклассификация (ObjC вообще не выполняет ковариантность) - говорит: " Эй, чувак, я кое-что сделал, вот твой результат, разберись с ним ».
Наличие непустых возвращаемых типов сделало распределенные объекты значительно менее эффективными. Непустой возвращаемый тип должен быть проксирован обратно по сети. Возврат (void)
не требует такого прокси, и (oneway void)
может быть отправлен по сети и выполнен асинхронно с локальной стороны, еще быстрее.
В любом случае, отвечу на ваш исходный вопрос: нет, вы почти никогда не вернете [[self keep] autorelease];
из такого контекста метода FLUENT-y. Как говорили другие, self
является своего рода особенным, и тот факт, что вы даже выполняете метод, означает, что self
будет существовать по крайней мере до тех пор, пока выполняется выполнение метода (потоки будут треклятый).
Я задал этот вопрос с тремя намерениями: проблема с памятью (автозапуск?), Использование self
и лучший метод использования шаблона Fluent Interface в ObjC. Спасибо за прекрасные ответы и вдумчивый анализ. Однако я не уверен. Я не согласен вообще избегать FI в ObjC.
Я думаю, что на самом деле это проблема дизайна и архитектуры. Если вы можете создать класс с хорошо структурированным шаблоном FI, вы увидите его красоту и удобство использования, а также простоту обслуживания.Хотя я новичок в ObjC и у меня не так много воспоминаний или опыта, я действительно вижу много хороших практик FI и их широкое использование во многих проектах с открытым исходным кодом, языках и фреймворках. Вот некоторые из моих аргументов:
Отладка вообще не проблема. Я не большой поклонник отладчика времени компиляции или отладки в Xcode, в случае ObjC. Я был бы потерян, если бы я делал несколько звонков и выходил из них. Я просто не могу вспомнить или отследить все состояния переменных или экземпляров. Кроме того, отладка во время компиляции сильно отличается от времени выполнения или в некоторых случаях просто невозможна. Например, события и потоки сводят вас с ума. Я думаю, что отладка во время компиляции просто тратит время и энергию, и это мое последнее средство. Если вы чувствуете боль при отладке FI, не вините FI, расслабьтесь и спросите себя, правильно ли вы делаете это? Вместо этого я бы использовал такие инструменты, как NSLog (), некоторые другие инструменты (например, мой собственный класс-оболочку MyLogger , еще не законченный, но скоро) или модульные тесты, чтобы помочь мне.
Unexpect nil
- очень интересная особенность ObjeC (не вызывает исключений). Если бы это случилось со мной, я бы не стал винить ФИ. У меня хороший дизайн, подходит ли FI для случая, только мои неосторожные или тупые баги в моих кодах? Я не полностью доверяю своим кодам, если не могу доказать это своими модульными тестами. Другими словами, я бы тратил время и силы на написание модульных тестов, охватывающих все случаи и логический поток. Делая это, я надежно охраняю не только текущие коды, но и будущие работы по обслуживанию или обновлениям.
Хороший дизайн и практика FI значительно упрощают рефакторинг. FI использовался во многих случаях, таких как подготовка сериалов к финальному действию, настройка, создание экземпляров и многое другое. Например, если у меня есть фрагменты кода для настройки чего-либо для подготовки с некоторыми временными переменными для действия, конкретной конфигурации или создания экземпляра результата, я бы подумал о том, чтобы инкапсулировать эти фрагменты в класс API FI с легкостью: понимайте методы как объединяемые в цепочки элементы. Класс предоставляет плавную и гибкую цепочку для выражения логики. Это не только удобно для чтения, но и для гибкого повторного использования и обслуживания.
FI - это связанный поток, один вызов сразу за другим. Это предотвращает любые случайные вставки или удаления. Просто представьте, что кто-то другой пришел к моим кодам и не полностью понимает подготовку и логический поток кодов фрагментов, случайные изменения могут сломать приложение. На первый взгляд это может выглядеть некрасиво , а может людям это с первого взгляда не понравится. Тем не менее, вы увидите преимущества и веские причины для хорошо спроектированного API FI, если захотите изучить, по возможности увидеть, что внутри класса, а также способы использования API. Я думаю, некрасивый
скрывает субъективный взгляд и отказывается учиться.
FI не является структурой фиксированного рабочего потока, как это делают многие фрагменты кода. FI на самом деле предоставляет гибкий способ связать небольшие и четко определенные действия или настройки вместе, чтобы охватить различные случаи. В случае фрагментов, которые трудно понять и / или поддерживать, FI может быть решением для преобразования кодов в легко читаемую логическую цепочку.
Не злоупотребляйте ФИ. Это наиболее частая причина создания уродливых кодов и трудных для понимания кодов. Если FI не подходит, не используйте его. FI не является идеальным решением, но он предоставляет шаблон для инкапсуляции промежуточных приготовлений и раскрытия гибкой логической схемы. Сделать хорошо спроектированный фреймворк или API-интерфейс непросто.Чтобы изучить его использование, нужны усилия. Вы взвешиваете плюсы и минусы.
Если у вас есть возможность переключиться на Objective-C 2 для вашего приложения, которое предлагает полную сборку мусора , вам не нужно беспокоиться о вызове функции сохранения / выпуска. / autorelease. (Объекты по-прежнему отвечают на эти сообщения, они просто ничего не делают).
Затем вы можете вернуть себя из своих беглых методов, и они будут очищены автоматически.
Приложения Objective-C 2 будут работать на Leopard (10.5) или новее.