среда, 17 октября 2012 г.

ARC и [[self retain] autorelease]


Иногда нужно быть уверенным, что объект просуществует как минимум до конца собственного вызванного метода. 
До ARC мы обычно писали в таких случаях [[self retain] autorelease]; (выглядит не очень приятно, согласен), но ARC в числе прочих забот принес и необходимость ввести какую-то замену этому способу.
Думаю, когда обычные разработчики оказываются перед подобной проблемой, они обычно спрашивают себя: "А что бы сделали разработчики из Apple?". К счастью, в данном случае разработчики Apple продемонстрировали свой вариант решения проблемы, на который я наткнулся просматривая обновленный код демо-проекта SimplePing.
Дабы не быть голословным приведу просто код. 
Итак, в теле метода, где self должен гарантированно существовать на момент завершения вызова метода:

 [self performSelector:@selector(noop) withObject:nil afterDelay:0.0];

Само же описание метода феноменально:
- (void)noop
{

}

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

четверг, 4 октября 2012 г.

Выстрелить в ногу на Objective-C

Поскольку Objective-C в корне своем - С, то можно просто выстрелить себе в ногу. Но это неинтересно.

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

Вспоминаем, что Нога не отвечает протоколу "ОбъектКоторыйМожетБытьЦельюВыстрела". Создаем протокол, прописываем реализацию обязательного метода "получилаВыстрел:".

Запускаем. Работает, но остается патрон на полу. Прибираемся. Вспоминаем времена до ARC, когда за уборкой каждого патрона приходилось следить самому. Ворчим на новое поколение стреляющих, которые не застали эти славные времена, поэтому управление патронами не понимают, в результате чего в случае появления патрона часто не знают, что делать вообще. 

Визуализируем. Для объектов создаем по несколько типов изображений: одно для iPhone, второе для iPhone с Retina-экраном, третье для нового iPhone c вытянутым экраном и еще одно для iPad. 

Следуя концепции MVC пишем контроллер - курок.

Портируем версию на Mac Os X (у всех классов интерфейса заменяем префикс UI на NS) . За время, пока мы пишем - выходит новое видео с WWDC, после просмотра которого оказывается, что в новой операционной системе есть библиотека выстрелов с картинками, методами и отличной документацией, а в разделе для разработчиков на сайте Apple есть проект-пример, в котором демонстрируется как выстрелить не только в ногу и не только себе. Плачем, потому что надо поддерживать iOS предыдущей версии. Видит око, да зуб неймет. Думаем, где взять время, чтобы посмотреть хотя бы половину роликов с новой конференции, когда еще с прошлой штук десять непросмотренных осталось.

Выходит новая операционная система и Xcode. Разработка замедляется - среда разработки явно работает медленнее, более требовательная операционная система подливает масла в огонь. Долгожданной функции вставки заголовков отсутствующих обязательных методов протокола в класс, который объявлен поддерживающим этот протокол все еще нет.

Вспоминаешь, как много написано, решаешь закоммитить проект. Синхронизируешь с github. Во время синхронизации Xcode сбоит и запарывает проект. С мыслями о том, что для лузеров как ты была, есть и будет теплая ламповая консоль, через которую и надо работать с git, берешь настоящий пистолет и тестируешь проект в реальных условиях с поправкой на голову вместо ноги.

@end

понедельник, 10 сентября 2012 г.

Недолгий век UISearchDisplayController


Объект UISearchDisplayController инициализируется вызовом initWithSearchBar:contentsController:. Как видно из названия метода и документации, работающему контроллеру необходимо задать searchBar и contentController, в виде которого будет отображаться таблица с результатами поиска.
Покопавшись в документации UIViewController мы также увидим read-only свойство searchDisplayController.
Это значит, что мы не можем задать нашему основному контроллеру searchDisplayController напрямую, вместо этого мы должны инициализировать объект UISearchDisplayController с основным контроллером.
Ситуация стандартная, таким же образом мы действуем, когда создаем UINavigationController - не назначаем его основному контроллеру, а инициируем объект UINavigationController с основным контроллером.
Что может ввести в заблуждение, так это то, что обладание ссылкой searchDisplayController достаточно для работы. Но это не так, потому что во избежание зацикливания ссылок контроллеров друг на друга объект UISearchDisplayController "умирает" спустя некоторое время после создания.
Говоря проще, создавая UISearchDisplayController во viewDidLoad вы теряете его сразу по завершению метода.
Выход прост: создать отдельное strong-свойство в основном контроллере для объекта UISearchDisplayController. Правда, остается непонятным, зачем тогда вообще ссылка searchDisplayController - ведь в отличие от UINavigationController он связан только с одним контроллером за раз.

понедельник, 16 июля 2012 г.

Core Data. Запрос с сортировкой не любит потоки.

Как известно, Core Data - отличный инструмент для работы с данными большого объема на уровне объектов. Core Data позволяет производить различные операции с контекстами на разных потоках, исходя из правила "один контекст на один поток".
Рядовая ситуация - контекст на главном потоке и "фоновый" контекст на фоновом потоке.
После проведения операция на фоновом потоке и сохранения, назначение sortDescriptors объектам NSFetchRequest не принесет ожидаемого результат. Мне не понятно, чем вызвано такое поведение, но факт остается фактом - использовать массив из NSSortDescriptor объектов для включения сортировки в запрос - тщетное дело. Информации по этому вопросу не очень много, но из того что я смог собрать стало ясно следующее:

  1. Поведение различно на iOS 4 и iOS 5 (iOS 6 не проверял).
  2. Некоторым помогает сохранение главного контекста.
  3. Необходимо следить за тем, чтобы контекст сохранялся на своем потоке. Иными словами, основной контекст всегда будет блокировать интерфейс приложения (поскольку существует на главном потоке).
  4. Если после сохранения контекстов (рекурсивное, сохраняя "фоновые" контексты, "вливая" изменения в основной контекст и сохраняя его в непосредственное хранилище данных) внести изменения в контекст (я добавлял новые объекты), сортировка запроса вновь начнет работать.
  5. Чтобы гарантировано получать сортированный массив можно сортировать массив, получаемый в результате выполнения запроса без сортировки.
  6. NSPredicate работает без проблем. Это прекрасно, потому что он куда важнее NSSortDescriptor (поскольку правильно описанный предикат - это эффективность SQL-запросов).
Кое-что по делу:

Автор смог решить проблему последовательным сохранением контекстов, реализованным в Magical Record. Я долго бился над пробным приложением и мой результат был менее радостным - сортировка не работала.

Более плачевный результат, когда выполнение запроса влекло за собой Bad Access. Автор не приводит код, поэтому есть вероятность, что это дело - результат неаккуратного обращения с памятью. Но тот факт, что сортировка готовых данных проходит без каких-либо ошибок указывает на то, что проблема имеет по крайней мере схожий корень с описанным выше случаем.

Непредсказуемый NSURLConnectionDownloadDelegate

NSURLConnectionDownloadDelegate сменил на посту NSURLDataDelegate () и привнес немало радости iOS-пограммистам, пытавшимся использовать новый делегатный метод – connectionDidFinishDownloading:destinationURL:, потому как получаемый URL указывал на несуществующий файл, в том случае, если файл имел расширение JPG или PDF.
Более подробно на Stackoverflow: