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

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

Непривычно видеть AppCode в статье про iOS.
Продолжайте
Честно сказать, только недавно постиг Core Data и использую в основном с SQLite базами данных.

Предположим, мне нужно будет хранить много картинок и много текста (а вдруг и видео?) — разумно ли будет все еще использовать SQLite или стоит задуматься о других типах хранения данных через CoreData? А какие есть?

А еще в некоторых туториалах видел автоматическое создание классов к каждой сущности в модели, но так и не нашел кнопочку в Xcode. А использовать valueForKey: не особо приятно (фу на эти магические стринги). Можете подсказать, куда копать ради этих самых авто классов?
Вам пригодится.
Видео, картинки, аудио я бы хранил на диске.

В «Практической части» я использую valueForKey: только потому, что этот метод используется в «Теоретической части». Не хотелось вводить людей в заблуждение. Всему своё время.
Спасибо!
Точно! :) Ведь в базе можно хранить просто ссылки на файлы
а еще есть опция «Store in External Record File». Тогда блобы более 100 кб Core Data сама будет выносить в файлы. И не надо будет подчищать их за собой.
мне казалось, что в 200х уже перевелись те, кто хранит все в БД :)
и как Вам AppCode? Есть свои плюсы минусы?
Для меня больше плюсов, чем минусов.
Нравится, а быть точнее — влюблен :)
Плюсы — намного более адекватные рефакторинги и вообще работа с кодом. Вставлять руками импорты в Xcode в 2013 году это meh.
Пост совсем о другом, но тем не менее, мне хотелось бы обсудить GDC и его использование в примере из статьи. Правильны ли мои рассуждения:

1) подобная конструкция не является безопасной, так как используется один и тот же managedObjectContext в разных потоках.
/    сохраняем данные в фоне, чтобы не замораживать интерфейс
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        for(NSDictionary *user in controller.userFriends){
            NSManagedObject *friend = [NSEntityDescription insertNewObjectForEntityForName:@"Friend"
                                                                    inManagedObjectContext:self.managedObjectContext];

            [friend setValue:user[@"first_name"] forKey:@"first_name"];
            [friend setValue:user[@"last_name"] forKey:@"last_name"];
            [friend setValue:[NSData dataWithContentsOfURL:[NSURL URLWithString:user[@"photo"]]] forKey:@"photo"];
            [friend setValue:user[@"status"] forKey:@"status"];

            NSLog(@"friend: %@", friend);
        }

        if([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:nil]){
            NSLog(@"Unresolved error!");
            abort();
        }

И по-хорошему следует создать отдельный NSManagedContext для бэкграунда? Или я все же ошибаюсь?

2) Этот код загрузки изображения в ячейку таблицы может работать некорректно.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSData *img;

        if([_userFriends[(NSUInteger) indexPath.row][@"photo"] isKindOfClass:[NSData class]]){
            img = _userFriends[(NSUInteger) indexPath.row][@"photo"];
        } else {
            NSString* imgPath = _userFriends[(NSUInteger)indexPath.row][@"photo"];
            img = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath]];
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image = [UIImage imageWithData:img];
        });
    });

Это связано с переиспользованием cell'ов в UITableView. Рассмотрим ситуацию, когда во время загрузки аватарки для 4 ячейки пользователь прокрутил таблицу вниз. Эта 4 ячейка (в блоке она фигурирует как cell) уже станет 10-15, из-за
[tableView dequeueReusableCellWithIdentifier:cellID]; и у нас получится так, что аватарка для 4 ячейки вдруг отрисовался у 10-15 ячейки.

1) Запись всегда происходит из одного потока, поэтому в данном примере это не так страшно.
2) Рассмотрим ситуацию: пользователь запускает приложение, перед ним таблица и аватарки загружаются. Во время загрузки пользователь крутит таблицу вверх и начинается загрузка аватарок для соответствующих ячеек. Так вот в то время, когда dequeueReusableCellWithIdentifier:cellID: вернет ячейку, которая была ранее использована, изображение в ней будет сперва «сброшено» на изображение по умолчанию (картинка с буквой S), а уже после загрузки изображения в фоне оно будет установлено в ячейке. Поэтому работает всё корректно.
Или я Вас не правильно понял?
В идеале, нужно расширить UIImageView, и хранить в нём ссылку, которая предполагается к загрузке.
Перед установкой изображения проверять, эквивалентны ли ссылки
1) Опасна не только одновременная запись из нескольких потоков. Что если из UI-потока во время этого сохранения сработает fault, например? Получим одновременное обращение к контексту из разных потоков.
2) Все-таки проблема есть. Если картинка успела загрузиться в ячейку, все произойдет как Вы написали, картинка просто сбросится на дефолтную и начнет грузиться новая. А что если cell.imageVIew.image =… сработает только когда ячейка УЖЕ используется для другого объекта? Тут есть два варианта:

  • Запоминать не ячейку, а index path, и устанавливать картинку, получая текущую ячейку по запомненному index path. Однако этот метод усложняется, если возможны вставки/удаления строк в таблице
  • Хранить в ячейке objectID объекта, который там сейчас отображается. Далее все просто: когда картинка загружена, просматриваем visible cells и ищем там ячейку с нужным objectID
  • И вот выше подсказывают, хранить в ячейке URL желаемой картинки
2) Действительно, прошляпил такой вариант развития событий. Учтем и, спасибо за замечания (Dreddik).
1) Если работать через gdc, то код из очереди будет выполняться последовательно в одном потоке (по умолчанию)
Стоп, только если 1. Вы гарантируете использование этого контекста только внутри очереди 2. Очередь serial, а не concurrent.

Но, учитывая оба условия, не проще использовать queue concurrency type и performBlock: или performBlockAndWait:?
GCD — Grand Central Dispatch, а не GDC :)
Очепятка же...)
Автор, большое спасибо за перевод. Пожалуйста, продолжай в том же духе! Хорошую работу проделал.
Вам спасибо за отзыв! Именно такие отзывы придают сил для последующих переводов!
НЛО прилетело и опубликовало эту надпись здесь
Спасибо большое за статьи. Не забрасывайте пожалуйста работу. Отдельное спасибо за хороший перевод.

P.S. Пробывал повторить приложение ваше шаг за шагом. Заткнулся на подключении библиотеки по работе с vk. У меня в проекте появились варнинги и проект падает при запуске, думаю что это из-за того что у меня sdk 7.0.
Благодарю за отзыв!

Насчет PS ничего толком не могу сказать, с тех пор версия Вконтакте SDK сильно видоизменилась и фиксов много было (см GitHub). Если всё таки решите добить, то пишите с вопросами в личку, постараюсь помочь.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.