Показаны сообщения с ярлыком objc. Показать все сообщения
Показаны сообщения с ярлыком objc. Показать все сообщения

2014-12-04

Callbacks vs promises

Добавил в текущий проект промисы. Для сравнения

До:
-(void) p_pluckHamsSinceLastPluckWithCompletion:(BarPluckHamsCompletion) completion
{
  NSDate * date = [NSUserDefaults standardUserDefaults].lastPluckDate;

  XXXHamPlucker plucker = ^(XXXPluckSession * session, XXXFoo *foo, XXXPluckConsumer consumer) {
    [session searchBoosSinceDate:date withFoo:foo completion:^(NSError *error, NSIndexSet * boos) {
      if (error)
        return consumer(error, nil);
      [Bar filterBoos:boos withFoo:foo completion:^(NSIndexSet *boos) {
        [session pluckHamsWithBoos:boos withFoo:foo completion:^(NSError * error, NSArray * hams) {
          if (error)
            return consumer(error, nil);
          [Bar filterHams:hams inFoo:foo completion:^(NSArray *hams) {
            consumer(nil, hams);
          }];
        }];
      }];
    }];
  };

  [self p_pluckHamsUsingPlucker:plucker completionHandler:completion];
}

После
-(XXXPromise *) p_pluckHamsSinceLastPluck {
  NSDate * date = [NSUserDefaults standardUserDefaults].lastPluckDate;

  XXXHamPlucker plucker = ^(XXXPluckSession * session, XXXFoo * foo) {
    XXXPromise * promise = [session searchBoosSinceDate:date withFoo:foo];
    return promise.thenPromise(^(NSIndexSet * boos){
      return [Bar filterBoos:boos withFoo:foo];
    }).thenPromise(^(NSIndexSet * boos){
      return [session pluckHamsWithBoos:boos withFoo:foo];
    }).thenPromise(^(NSArray * hams) {
      return [Bar filterHams:hams withFoo:foo];
    });
  };

  return [self p_pluckHamsUsing:plucker];
}

Промисы свои, написал часа за 3-4. Смотрел RXPromise и PromiseKit, но они не умеют то, что мне нужно.

2013-11-25

Dot syntax in Objective C

По рсс приползло прекрасное (или ужасное, как посмотреть).
Люди делают в Objective-C вызов метода через точку. К примеру, a.plus(b) вместо [a plus:b] :
Заводят рид-онли свойство plus, которое возвращает блок (ака лямбда), который зовет у self метод plus: с переданным параметром.
Получается (a.plus)(b).

PS. Смотреть тут

2013-04-18

продолжаем извращаться

Еще один вариант "типизированных" коллекций в Objective C
#define NSArray_(T) NSArray
#define NSDictionary_(K,V) NSDictionary
NSArray_(NSString) * strings = …
NSDictionary_(NSString,NSString) * dict = 

2013-04-15

Немножко извращений с Objective C

1. Ехал лямбда через лямбда... 
Реальный код, убрал несущественное...
- (void) processItems:(NSDictionary *) items
                using:(void (^)(Item * item)) processor
{
    [self runBlock:^() {
        NSError * error = [self catchNSError:^() {
            [items enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                [obj enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                    processor(obj);                                        
                }];
            }];
        }];
    }];
}

2. Типизированные коллекции в Obj-C
NSArray/*<NSString*>*/ * strings = ...
NSDictionary/*<NSString*,NSString*>*/ * dict = ...

2013-02-14

Маконенависти очередной псто

Читая RSS, наткнулся пост "что вам нравится в МакОС" на ДОУ. Первым пунктом было:
 Типографика. Алгоритм сглаживания шрифтов в Mac OS настолько хорош, что я искал и ставил специальную программу для Windows, «чтобы было похоже».
На что моей первой реакцией было "лолчто?!" Сглаживание шрифтов в МакОС (а особенно в XCode) меня дико раздражает.  Кстати, интересно, как AppCode удается быть менее ШГ,  используя тот же шрифт, что и XCode?.
Ах да, наверное, если бы у меня был не обычный монитор, а Apple Cinema или MacBook c уриной ретиной, я бы считал по-другому.

 "Гуй застрял в 90х! -- Вы хотите странного и это не нужно".
Развернуть окно на пол-экрана (фича, которая из маргинальных тайловых менеджеров давно перекочевала в гламурный гуй вроде Unity или Win7) -- "А это нужно? Можете описать юзкейс?" Хотя и привели в комментах парочку программ (одну платную, и одну бесплатную), которыми это можно сделать. При этом на сайте бесплатной про клавиатурные хоткеи написано 2 предложения, все остальное -- touchpad gestures).
Развернуть окно, свернутое в док -- "Используй силу мышку, Люк". Ctrl+F2, W, вниз до конца, выбрать из списка, Enter -- весьма эргономично, да. Да и в целом -- мышкой возить приходится на порядок больше, чем в Windows/Linux, что раздражает.
Наверное, будь у меня не трекбол, а Magic Mouse или Magic Trackpad, я бы считал по-другому.

Маковские хоткеи.. О... Особенно в альтернативно одаренных программах вроде XCode. Вообще, разработчиков XCode после смерти в аду будут ждать какие-нибудь дешевые раздолбанные Chicony (нормальных old-school Mitsumi они не заслужили), на которых они должны будут перенабрать код XCode в XCode. И никаких мышей, не говоря уже про тачпады. Попробуйте переключиться на другой файл с клавиатуры. "Используй мышку, Люк".
Поведение Home/End/PgUp/PgDown -- не такое, как в Win/Linux. Think different.
Command, которая основная мета-клавиша, обычно забиндена на Windows key. После месяца-двух работы под OSX начинает болеть левая рука.
Ах да, будь у меня не эргономичная микрософтовская клава, а Apple Keyboard, я бы считал по-другому.

Неспешность. Как оказалось, я не один такой, у которого более-менее современный мак (i5, 4 GB DDR3 1600) тормозит при одновременно запущенных XCode и AppCode. Которые съедают ~500 Mb  и 1 Gb памяти соответственно. При этом free memory ~ 100-200 Mb, inactive memory ~ 500 Mb, но своп в 2-3 Gb присутсвует. Ах да, будь у меня больше памяти и SSD винт, я бы считал по-другому.

iTunes. Помню время, когда он был пусть и перегруженным комбайном, но еще вполне юзабельным. Я даже им пользовался под Windows. А сейчас... Можно поставить другой плейер, но чтобы iTunes не запускался по нажатии кнопки play на клавиатуре, нужно патчить или переименовывать бинарник одного из системных сервисов.

Objective-C. Попытка скрестить скорость C и ООП-идеологию SmallTalk. Язык, который был неплох на момент своего изобретения, но сейчас... Да, язык развивается, ручное управление памятью ушло в прошлое, уступив ARC, появились блоки ака замыкания. Только вот GC и блоки были в исходном Smalltalk-80.
Отдельно доставляет многословность языка. Когда пишешь (утрирую):
NSMutableArray * mutableArray = [NSMutableArray mutableArrayWithCapacity:...];
так и хочется откомментить: "DRY? Не, не слышал."
Ну, и имхо,
fs::path prefix = "/usr";
fs::path bin_prefix = prefix / "bin";
читабельнее, чем
NSString * prefix = @"usr";
NSString * bin_prefix = [prefix stringByAppendingPathComponent: @"bin"];

Когда в rss-ленте читаешь про хаскель, алгебру типов, зависимые типы, Agda/Coq/Идрис, монады там всякие и прочий матан, а потом пишешь код на слабо-типизированном языке, который местами даже "stringly-typed" --  где аналог LINQ выглядит примерно как ...
NSArray * expired = [tasks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"expirationDate < %@", [NSDate date]]];
Когда пишешь на динамическом языке без REPLа...
Когда в динамическом языке при некоторой доле невнимательности все же можно добиться SIGSEGV, таки разыменовав нулевой указатель...
Когда читаешь про transactional memory и минимизацию сайд-эффектов для распрараллеливания, а потом работаешь с обьектами, для которых не то что модифицировать, но даже читать(Привет, CoreData!) свойства в других потоках нельзя...
Это грустно.



Ах да, если бы меня покусал Стив Джобс, я бы считал по другому. :)

Справедливости ради, к плюсам OSX можно отнести
- наличие Unix окружения,
- предустановленные perl/python/ruby
- ненужность антивирусов

2012-08-31

Элементы ФП в ObjC (BlocksKit)

Когда-то жаловался, что ObjC у массива нету поиска по условию.
Нашел фреймворк, который позволяет писать на ObjC "функциональненько".
3 типа map (each:, apply:, map:), filter (select:, reject:), find(match:), any, all, none, fold (reduce:withBlock:). Плюс in-place варианты для мутабельных коллекций (performMap:, performSelect:, performReject:). Все принимают блок (ака лямбда).
Имхо, эта штука должна быть в ObjC "искаропки".

Юнит-тесты и iOS.

Хотел в новом проекте под ios сделать все кошерно -- отделить логику от гуя, обложить логику юнит-тестами. Ага, щаз.
Вынес логику как статическую либу -- потерялись некоторые методы. Проект компилируется без ошибок, а в рантайме падает - Unrecognized Selector. Как оказалось, без флага -ObjC линкер не подтягивает категории (аналог extension methods из C#) из статических библиотек. Благо сотрудник уже с таким сталкивался, подсказал.
Начал пилить тесты. Инициализация MagicRecord (библиотека для active record) падает с исключением "URL is nil".  Не находит схему данных. Смотрю внутрь бандла теста - лежит. Оказалось, она пытается загрузить схему из ресурсов главного модуля. А для юнит-тестов главным модулем является программа otest из SDK.

Upd. Юнит-тесты поборол. Решение со StackOverflow:

Assuming you have an application target called "MyApp"
  1. Add a new target of type "other/Cocoa Unit Testing Bundle" to the project e.g "MyAppTesting". Here all Unit test files are located.
  2. Go to MyAppTesting Build Phases and add MyApp as Target Dependency. This assures that MyApp is build before building the MyAppTesting target.
  3. Open the Build Settings of MyAppTesting and change
    • Bundle Loader: $(BUILT_PRODUCTS_DIR)/MyApp.app/MyApp
    • Test host: $(BUNDLE_LOADER)
    That causes the tests to run within MyApp.
  4. Open the Build Settings of MyApp and change
    • Symbols Hidden by default: NO (for both)
    • Strip debug Symbols during Copy: Debug:NO
    By doing so you do not have to include every .m-file into the test target.

2012-05-10

Ruby+iOS

Нашел интересный проект: www.rubymotion.com

Компилятор (!) диалекта Ruby, построенного поверх ObjC runtime.
Теперь под iOS можно писать на нормальном языке с преферансом и гимназистками богатой стандартной библиотекой, лямбдами, DSL, REPL и другими нямками.
При этом оно понимает любые objc-фреймворки и позволяет почти прозрачно вызывать сишные функции.

Из минусов:
  1. хотят много денег ( $200 $150) 
  2. использует ARC, поэтому циклические ссылки не разруливает :

в копилку ненависти к objc

У NSArray отстуствует метод поиска по предикату. Есть filteredArrayUsingPredicate: и filterUsingPredicate: (ну да,  длинные имена -- наше все) есть, а какого-нибудь findFirstUsingPredicate: - нету.

вот и плодятся по коду кусочки вида

id object = nil;
for (id x in array)
  if (condition(x))
     break;

Казалось бы, четыре строчки вместо одной, да и добавить такой метод -- не проблема, но раздражает, что нет "искаропки".

Да и предикаты в obj-c -- это нечто.
Пытались сделать LINQ, а получили недо-SQL в стрингах.

Справедливости ради стоит заметить, что в языке есть "блоки" (которые почти лямбды из С++11), и предикаты можно создавать на их основе.



2012-04-25

ObjC vs Java

Серия постов о том, чего, по мнению Java-программиста, не хватает в ObjectiveC.
Ну, и мои мысли по этому поводу.
TL;DR: Отсутствие namespace и ручное управление памятью -- зло. ObjC -- не мой любимый язык.
  1. дженерики  для динамического языка не очень критичны (в питоне, к примеру, их нет, а в ActionScript -- есть всего один дженерик)
  2. Нет абстрактных методов. Согласен. С точки зрения "классического" ооп, отсутствие abstract и protected методов не дает задать контракт между базовым и дочерними классами. Приходится извращаться с методами, бросающими исключение -- что работает только при нормальном покрытии юнит-тестами. 
  3. Хидер+Реализация(*.h/*.m) vs один файл. Спорное высказывание. Хотя с одним файлом работать и удобнее, наличие отдельно вынесенного публичного интерфейса есть хорошо.
  4. Пространства имен. ППКС. Отсутствие пакетов или пространств имен в объектно-ориентированном языке -- это, мягко говоря, извращение. 
  5. Исключения.  В ObjC они более "многословны", чем в других языках, на этом, имхо, все отличия и заканчиваются. 
Из других минусов языка автор не назвал многословность (да-да, тот самый синтаксический оверхед :) ). Возможно, потому, что по сравнению с явой он не столь велик.
Защитники ObjC скажут "зато имя метода отражает, что он делает", но методы вида
- (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex или константы длиной в 65 символов это тот случай, когда, сфокусировавшись на деревьях, не видно леса.

Кроме длинных идентификаторов, многословность вызвана еще одним недостатком языка:
Авторы языка так и не смогли определиться с управлением памятью.

В Managed языках все просто -- там изначально есть GC.

В C++ есть деструкторы объектов на стеке, что привело к концепции "умных" указателей: сперва пусть кривой, но стандартный std::auto_ptr, потом появились труды Александреску, бустовкие intrusive_ptr и shared_ptr/weak_ptr, которые и стали стандартом сначала де-факто, а потом и де-юре.
Move-semantic в С++11 с одной стороны, была революцией (как писали авторы стандарта -- "мы сломали почти все книги о эффективном программировании на С++"), с другой -- слегка поменялась форма, но не содержание -- заворачиваем объект в умный указатель и расслабляемся.

В ObjC же управление памятью развивалось этапами.

Изначально временем жизни управлял сам программист, создавая объект через метод класса alloc и управляя его жизнью, увеличивая счетчик ссылок через retain и уменьшая его вызовом release. То есть ручное управление временем жизни объектов. И сдесь разработчики допустили еще одну ошибку: они сделали создание объекта двухфазным: сперва выделение памяти (обычно alloc), потом инициализация (init или initWith...).  Изначально это было сделано для поддержки кастомного распределителя памяти (allocWithZone:), но, по-моему, этим никто никогда не пользовался. Получаем запись вида
Type * x = [[Type alloc] initWith:y];

И тут появляется хитрая политика владения: если в названии метода есть слова copy, alloc, new, то метод возвращает объект с увеличенным счетчиком ссылок, иначе -- с тем-же. То есть, налицо попытка решения технической задачи административным путем.  
Ну, и проблему циклических ссылок никто не отменял. 

Потом появилась концепция Autorelease Pool. Объекты, зарегистрированные в нем, удаляются при очистке пула, если на них больше никто не ссылается. Пулы образуют свой стек, что позволяет при выходе из метода, к примеру, подчистить за собой (ну, если не забыть вызвать у пула drain или release];
У стандартных классов появились методы вида [Type typeWithY:y], возвращающие объект в полумертвом состоянии (уже зарегистрированным в текущем авторелиз пуле) 
И вместо того, чтобы переделать стандартную библиотеку, объявив цепочку alloc-init deprecated, разработчки языка пошли по легкому пути и просто добавили autorelease в базовый класс.
А теперь сравните код

TypeX * x = [[[TypeX alloc] initWithTypeY:[[[TypeY alloc] initWithY:y] autorelease]] autorelease];
и 
TypeX x = TypeY(y); // Если на стеке

// или, если объекты выделять в куче.
std::shared_ptr<TypeX> x = std::make_shared<TypeX>(std::make_shared<TypeY>(y));


В ObjC 2.0 добавили свойства, что позволило не копипастить типичный для геттеров и сеттеров код. Но управление членами класса осталось на том-же уровне (retain в init, release в dealloc, если забыл - то ССЗБ)

Потом был GC, который объявили deprecated быстрее, чем он стал мейнстримом. Ну, не deprecated, но "ARC is preferable now". Automatic Reference Counting - это автоматическая вставка retain/release, если статический анализатор Clang считает, что они тут нужны. Если имена ваших методов следуют административным ограничениям (увеличивающие кол-во ссылок методы содержат слово alloc, copy, new, retain ; не меняющие -- не содержат; плюс несколько макросов/ключевых слов типа __strong или __weak), то все будет хорошо.

Если у вас есть циклические ссылки или кто-то нарушил правила  - ой. Хорошо если просто утечка, но может быть и дабл-фри. Который крайне тяжело отлаживать, ибо падает обычно внутри Autorelease Pool.

После того, как некоторое время пописал на ObjC, дико хочется обратно на C++.