Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Избавляемся от строковых констант в Objective-C

Anna | 2.07.2014 | нет комментариев
Магические константы в коде — зло. Строковые константы в коде — еще большее зло.
И как бы бы от них никуда не денешься, они повсеместно:1) При загрузке объектов из xib-ов:

MyView* view = [[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil] lastObject];
MyViewController* controller = [MyViewController initWithNibName:@"MyViewController" bundle:nil];

2) При работе с CoreData:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"MyCoreDataClass" inManagedObjectContext:moc]];
[request setSortDescriptors:@[ [[NSSortDescriptor alloc] initWithKey:@"someProperty" ascending:NO] ]];

3) Если вы используете KVO, то строки возникают и здесь:

[self addObserver:someObservedObject 
       forKeyPath:@"someProperty"
          options:(NSKeyValueObservingOptionNew |  NSKeyValueObservingOptionOld) 
          context:nil];

4) Ну и KVC:

NSInteger maxValue = [[arrayOfMyClassObjects valueForKeyPath:@"@max.someProperty"] intValue];

5) Но даже если CoreData вы выбираете работу с SQLite напраямую, xib-ами вы пренебрегаете, то вот такой код вам должен быть знаком:

[self.tableView dequeueReusableCellWithIdentifier:@"MyTableViewCell"];

6) Ну и когда Apple представила миру Storyboard — это было восхитительно, если-бы не одно но:

[self performSegueWithIdentifier:@"MySegue" sender:nil]
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:( id )sender {
   if ( [segue.identifier isEqual:@"MySegue"] );
}

Вы видите загвоздку? Она состоит в том, что компилятор никак не проверяет содержимое строк, от того что не знает (да и не может в тезисе знать), что в них содержится. И если вы опечатаетесь либо измените значение соответствующих полей в xcdatamodel / xib / storyboard / переименуете property, то оплошность вылезет не на стадии компиляции, а в рантайме, и отловить и поправить ее будет дольше и подороже.
Так что-же дозволено сделать?
С некоторыми строками дозволено совладать административными мерами, с некоторыми — при помощи особого инструментария.

Загрузка из xib-ов

Скажем, если начать с правила, что имя xib-а должно совпадать с именем класса, тот, что он содержит, то код из первого примера дозволено переписать так:

MyView* view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([MyView class]) owner:self options:nil] lastObject];
MyViewController* controller = [MyViewController initWithNibName:NSStringFromClass([MyViewController class] bundle:nil];

Плюсом такого решения будет то, что если мы надумаем переименовать наш класс ( Xcode -> Edit -> Refactor -> Rename оставив галочку «Rename related files» выбранной ), то его при переименоввании xib будет так-же переименован, и, соответственно, загрузка вью/контроллера никак не пострадает.

CoreData

С примером 2 решение чуть труднее и комплекснее.
Для начала нам потребуются MagicalRecord и mogenerator

Если вы трудитесь с CoreData и все еще не используете эти восхитительные инструмменты, то самое время начать.
Добавим MagicalRecord в наш podfile (ну либо по старинке скопировав файлы в план — за подробностями в гихаб)

pod MagicalRecord

И установим mogenerator:

brew install mogenerator

Либо скачаем установщик с сайта и поставим его вручную.

mogenerator создает на основе файла модели CoreData начальные файлы, которые напротив пришлось бы писать руками.
Сейчас нам необходимо настроить план так, Дабы запускать утилиту при всякой сборке.
Project -> Targets -> Add Build Phase -> Add Run Script:
Идем в настройки таргета, в закладку Build Phases и добавляем новую фазу в виде скрипта:
image
Ну и сам скрипт:
image
На одном ярусе с нашей моделью необходимо сделать две папки Human и Machine. Жмем CMD B — позже чего добавляем эти две папки в план. Они будут содержать в себе сгенерированные файлы модели из CoreData.

KVO и KVC

Еще одна вещь в Objective-C, которая энергично использует строковые константы — KeyPath для KVO и KVC. Дозволено применять упомянутый выше mogenerator. Если у нас в модели есть класс MyCoreDataClass, то mogenerator сделает конструкции MyCoreDataClassAttributes, MyCoreDataClassRelationships и MyCoreDataClassFetchedProperties. Так что сейчас дозволено переписать пример № 2 так:

NSArray* arr = [MyCoreDataClass MR_findAllSortedBy:MyCoreDataClassAttributes.someProperty ascending:NO inContext:moc];

А пример № 3 так:

[self addObserver:myCoreDataClass
       forKeyPath:MyCoreDataClassAttributes.someProperty
          options:(NSKeyValueObservingOptionNew |  NSKeyValueObservingOptionOld) 
          context:nil];

Но такое решение подойдет только для CoreData. Нам же необходимо что-то больше всеобщее.

Для этого абсолютно подойдет библиотека Valid-KeyPath:

#import "EXTKeyPathCoding.h"

[self addObserver:myClass
       forKeyPath:KEY.__(MyClass, someProperty)
          options:(NSKeyValueObservingOptionNew |  NSKeyValueObservingOptionOld) 
          context:nil];

Либо EXTKeyPathCoding библотеки libextobjc:

pod libextobjc
#import "MTKValidKeyPath.h"

[self addObserver:myClass
       forKeyPath:@keypath(MyClass.new, someProperty)
          options:(NSKeyValueObservingOptionNew |  NSKeyValueObservingOptionOld) 
          context:nil];

Плюсами обоих решений будет autocompletion из IDE во время написания самого кода, а также проверка самого KeyPath на стадии компиляции.

Если вы используете KVC, как в примере 4, то для генерации KeyPath вы можете воспользоваться всякий из библиотек, представленных выше, а можете — всякий из LINQ-like библиотек для Objective-C, скажемLinqToObjectiveC

pod LinqToObjectiveC
NSInteger maxValue = [arrayOfMyClassObjects aggregate:^(MyClass* myClass, NSInteger aggregate){
   return MAX( [myClass.someProperty intValue],  aggregate);
}];
Storyboard

Что касается UI части, то здесь нам поможет утилита ssgenerator Скачиваем ее отсель
Создаем для нее еще один скрипт в плане:
image
Позже чего добавляем в план сгенерированные файлы StoryboardSegue.h и StoryboardSegue.m. В этих файлах содержатся категории для тех контроллеров в сториборде, которые содержат в себе UIStoryboardSegue либо UITableViewCell, для которых определены идентификаторы. Сейчас дозволено применять:

[self.tableView dequeueReusableCellWithIdentifier:self.cell.MyTableViewCell];
[self performSegueWithIdentifier:self.segue.MySegue sender:nil]
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:( id )sender {
   if ( [segue.identifier isEqual:self.segue.MySegue] );
}

Завершение

Освобождение от строковых констант в коде — не самоцель, а метод значительно сэкономить на написании и поддержке кода за счет проверок во время компиляции. Часть описанных методов требует сторонних утилит, часть — сторонних библиотек. Для некоторых программистов описанные методологии покажутся «трудными» и «плохочитаемыми», но все они направлены на как дозволено раннее обнаружение ошибок в коде. Так что если вы пишете код, тот, что не изменяется, код, в котором нет ошибок — эти методы не для вас.

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

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB