Как стать автором
Обновить

Комментарии 23

тяжело застабать синглтон

Извините, что сделать?

Не раскрыто, почему это удобней, чем реализовать например у синглота метод sharedInstance, где с помощью dispatch_once будет по требованию создаваться один экземпляр. А для тестов(если для тестов нужна другая инициализация) либо сделать sharedInstanceTest, либо закрыть инициализацю с помощью директив препроцессора и котролировать это конфигурациями сборки (хотя не очень элегантный вариант).
В общем не очень убедительно.
Я не нашел на что заменить слово «застабать».

sharedInstanceTest, #ifndef'ы, еще можно категорию написать, которая перекроет метод или добавит метод setSharedInstance — это все, имхо, костыли.

Удобство свойства в том, что всякий раз в тесте можно указать новый мок-объект, который будет делать то что нужно.
Если не находите нормального перевода, то лучше в криво переведенном слове в скобках укажите оригинал или непосредственно предложение, в котором оно используется в оригинале.
Сам так делаю, когда про Core Data перевожу.
Поправил.
//cc rule
stub [stʌb] — заглушить

Создать заглушку для синглтона.
Инициализаторы помогают избавиться от различных shared* методов и хардкода, описанного в начале статьи

Скорее всего не избавляет, а переносит их определения в другой конфигурационный файл. Программисту обычно легче работать видя полностью исходный код в файле, а не переключаясь между исходным кодом и конфигурационным файлом. Но, конечно, данный подход преимущество при создании моков дает.
Тут суть в том, что какой-нибудь ViewController не видит shared* метода, а просто пользуется своим свойством. Избавление от этих методов скорее влияет на код конечного пользователя (ViewController в данном случае), а не на код самого синглтона. Можно без проблем оставить метод sharedInstance в самом классе и возвращать его в блоке инициализации.
Я хотел сказать то, что в данном случае вместо того, чтобы перегрузить свойство в .m файле, нам надо определять его в конфигурационном файле. Случаи, когда объект создается просто с помощью [MyClass new] не так уж часты, как хотелось бы.
Не соглашусь, у меня есть масса сервисов которые именно так и создаются: ProgressViewService, ImageLoaderService, SomeResourceLoader и т.д.
Почти в каждом приложении есть некая сущность, экземпляр которой должен быть единым в системе, но это не значит, что эту сущность надо проектировать как синглтон.

Мне кажется, это даже вредно, и надо противиться всякому «синглтоновому» ходу мысли. Сущность надо проектировать с расчётом на то, что её экземпляр будет одним из многих. Чтоб потом гипотетически как можно менее трудозатратно перетащить этот класс в другой проект.

Уже позже, синглтон из сущности можно сделать двумя способами:
1) В приложении уже есть один синглтон, от которого не уйдешь — апп делегат. Достаточно добавить туда рид-онли свойство, и создать категорию над сущностью с методом sharedInstance, который вытаскивает это свойство из апп делегата. Инициализация свойства в апп делегате будет ленива. (Этот способ подойдет, когда класс сущности потом не будет «библиотечным»)
2) Если класс сущности библиотечный, удобнее всего метод sharedInstance делать через dispatch_once

Таким образом синглтон — это просто дополнительная фича к классу, не более.

Страшная история: один раз от индусов в руки попал проект, где каждый из, приблизительно, 30-ти UIViewController'ов был синглтоном.
При реализации синглтона через AppDelegate вылезет другая беда: повысится связность классов. То есть по сути, об этом объекте в программе будут знать все, кому не лень.
неа, #import «AppDelegate.h» будет только в файле Entity+App.m

а вот куда втыкать #import «Entity+App.h» (где нужно или в .pch) — решать разработчику.
В моем случае этот вариант не очень подходит, т.к. я всячески против хранения каких-либо данных в AppDelegate, да и к категориям отношусь не очень хорошо.
понимаю, есть доля здравого смысла, чтобы не хранить данные в апп делегате. Кроме того, завязывание на него выглядит не очень красиво, когда есть gcd.

а разверните, почему к категориям плохо относитесь?

Мне, например, по-кайфу писать код вроде:

pageURL = rawURLString.URL.URLWithoutAnchor.URLWithoutParameters;

return [url URLByModifyingParams:^(NSMutableDictionary *params) {
params[@«per_page»] = @«48»;
}];


components = [components map:^WAComponent *(WAComponent *oldComponent) {
return [oldComponent.assumptionID isEqual:component.assumptionID]
? c.component
: oldComponent;
}];


— (BOOL)isTerminalSessionStatus {
return
[StoredSession.terminalSessionStatuses firstObjectWithPredicate:^BOOL(NSNumber *s) {
return [s isEqual:self.status];
}]? YES: NO;
}

очень удобно. Получается что-то вроде функционального стиля.
Не знаю, что бы я делал без категорий :)
Насмотрелся «плохих» категорий, да и любую задачу можно решить без их использования. Мне тяжело понять _зачем_ ими пользоваться :)
Я и сам ими пользуюсь, но только там, где, как мне кажется, это уместно.
А вообще тема довольно холиварная, на вкус и цвет :)
На вскус и цвет — это да. У меня большинство наработок, которые кочуют из проекта в проект выполнено в виде категорий.

Приведу свои аргументы «зачем». Например, мне кажется, удобнее, чем наследоваться от NSURL или писать дополнительный класс, поиметь у того же NSURL методы URLWithoutAnchor, URLWithoutParameters, — (NSURL *)URLByModifyingParams:(void(^)(NSMutableDictionary *params))modification.

Конечно, мы можем воспользоваться обычной функцией и передавать объект первым аргументом. Но кажется a.b красивее, чем b(a) и [x y:z p:q] красивее, чем yp(x, z, q).

У категорий есть полезное ограничение — отсутствие инкапсулированного состояния **. Т.е. если вы решили расширить сущность и instance variable не требуется, категории — красивый способ это сделать. Видишь унаследованный класс — сразу понимаешь, что внутри может быть какое-то состояние. Видишь категорию — только дополнительные методы.

хороший пример — BlocksKit. Например, UIAlertView — github.com/pandamonia/BlocksKit/blob/master/BlocksKit/UIKit/UIAlertView%2BBlocksKit.h

** — на самом деле мы можем любой стейт ассоциировать с любым объектом в рантайме и обойти это ограничение, но это не так удобно, неочевидно и требует objc/runtime.h
Может быть. но должно ли оно там быть?
Ну это спорный вопрос, конечно.

Но подобную штуку без associated objects не сделать например:
github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoaFramework/ReactiveCocoa/UIRefreshControl%2BRACCommandSupport.m

Точнее сделать-то можно, но будет не так удобно.

UPD: Хотя это сложно назвать именно состонием, как раз associated object и есть.
Мне, например, по-кайфу писать код вроде: pageURL = rawURLString.URL.URLWithoutAnchor.URLWithoutParameters;

В данном случае уровень вложенности зашкаливает.
Буквально вчера была добавлена возможность использовать протоколы, не только конкретные классы
Раз lazy_initializer исполняется вне main, то надо бы его код обернуть в @autoreleasepool{...}, иначе очень легко допустить утечку.

Я для той же цели обычно реализую метод +load у AppDelegate, в Obj-C это как-то привычнее.
Да, вы правы.
Для метода +load нужно создавать класс, а lazy_initializer позволяет иметь только один .m файл.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории