SlideShare una empresa de Scribd logo
1 de 38
Descargar para leer sin conexión
НИЧЕГО
ОСОБЕННОГО
НУ ПРАВДА, НИЧЕГО ТАКОГО
ИСТОРИЯ 1
Дано: у клиента стабильно повторяющийся краш, а у нас,
конечно же, все работает.
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x18ef211b8 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x18d95855c objc_exception_throw + 56
2 CoreFoundation 0x18edfc71c -[__NSArrayM objectAtIndex:]
3 BeautifulApp 0x10005ce4c -[BAWebSerivce ...]
ИСТОРИЯ 1
Дано: у клиента стабильно повторяющийся краш, а у нас,
конечно же, все работает.
Источник горя по крашлогу:



(но у нас-то все нормально)
- (void) dataProvider:(id) dataProvider
didFinishLoadingTasks: (NSArray<TFTask*>*) tasks
activeTaskIndex: (NSInteger) activeIndex
{
//...
self.tasks = tasks;
self.activeTask = tasks[activeIndex];
//...
}
ИСТОРИЯ 1
Дано: у клиента стабильно повторяющийся краш, а у нас,
конечно же, все работает.
Тем временем, ПМ, поставивший версию с тестфлайта,
воспроизводит баг с завидной регулярностью.

Чем отличаются билд у разработчика на телефоне и билд у
клиента в тестфлайте?
ИСТОРИЯ 1
Дано: у клиента стабильно повторяющийся краш, а у нас,
конечно же, все работает.
Тем временем, ПМ, поставивший версию с тестфлайта,
воспроизводит баг с завидной регулярностью.

Build configuration: Debug vs. Release
ИСТОРИЯ 1
И действительно,
int index;
//...
if ([jsonData[kHasActiveSelection] boolValue]) {
index = [jsonData[kSelectedItem] integerValue];
}
ИСТОРИЯ 1
И действительно,
int index;
//...
if ([jsonData[kHasActiveSelection] boolValue]) {
index = [jsonData[kSelectedItem] integerValue];
}
int index;
ИСТОРИЯ 1
Сейчас таким свойством обладают только элементарные типы (и
компилятор по-прежнему не предупреждает, если только это не
SEL). То есть встретиться с этой проблемой можно только используя
непроинициализированные индексы или не-ARC указатели
(например, на CoreFoundation-объекты или просто void* буферы)
Но до появления ARC эта проблема касалась в том числе и любых
объектов, указатели на которые создавались на стеке, и очень легко
было пострадать:
NSError* error;
[_managedObjectContext save: &error];
if (error) {
NSLog(@"error: %@", error);
}
ИСТОРИЯ 1
А вообще различия между Debug и Release конфигурациями
таят в себе много незабываемых и веселых моментов
ИСТОРИЯ 1
А вообще различия между Debug и Release конфигурациями
таят в себе много незабываемых и веселых моментов:
NSAssert([[self managedObjectContext] save: nil],
@"Error saving context");
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• Дано: приложение сохраняет данные в папку документов, но
при следующем запуске приложения сохраненные данные
куда-то исчезают.
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• Дано: приложение сохраняет данные в папку документов, но
при следующем запуске приложения сохраненные данные
куда-то исчезают.
• Медиаданные хранятся в виде файлов, пути к файлам
хранятся где-то неважно где (пусть хоть в NSUserDefaults)
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• Дано: приложение сохраняет данные в папку документов, но
при следующем запуске приложения сохраненные данные
куда-то исчезают.
• Медиаданные хранятся в виде файлов, пути к файлам
хранятся где-то неважно где (пусть хоть в NSUserDefaults)
• Ошибок при записи нет. Все корректно записывается.
Ошибка при чтении есть: файл не найден. Как так не найден-
то, когда вот только что он нормально записался и путь тот же
самый?
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• file:///Users/iosdeveloper/Library/Developer/CoreSimulator/
Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/
Containers/Data/Application/8AB0AAF5-483A-4A21-
B503-86CFCE78C034/Documents/
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• file:///Users/iosdeveloper/Library/Developer/CoreSimulator/
Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/
Containers/Data/Application/8AB0AAF5-483A-4A21-
B503-86CFCE78C034/Documents/
• Снаряжаем экспедицию
ОБЫЧНАЯ ИСТОРИЯ ДВА
ИСТОРИЯ ДВА
• file:///Users/iosdeveloper/Library/Developer/CoreSimulator/
Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/
Containers/Data/Application/8AB0AAF5-483A-4A21-
B503-86CFCE78C034/Documents/
• Снаряжаем экспедицию
• file:///Users/iosdeveloper/Library/Developer/CoreSimulator/
Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/
Containers/Data/Application/8AB0AAF5-483A-4A21-
B503-86CFCE78C034/Documents/
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Дано: при клике на ячейку модальный вьюконтроллер
стабильно показывается со второго раза.
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Дано: при клике на ячейку модальный вьюконтроллер
стабильно показывается со второго раза.
• Удаляем все лишнее. Проблема сохраняется даже в проекте
на 20 строчек.
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Дано: при клике на ячейку модальный вьюконтроллер
стабильно показывается со второго раза.
• Удаляем все лишнее. Проблема сохраняется даже в проекте
на 20 строчек.
• Замечаем, что иногда вьюконтроллер показывается все-таки
с первого раза, но с большой задержкой. Или при попытке
повернуть телефон. Или потрясти.
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Дано: при клике на ячейку модальный вьюконтроллер
стабильно показывается со второго раза.
• Удаляем все лишнее. Проблема сохраняется даже в проекте
на 20 строчек.
• Замечаем, что иногда вьюконтроллер показывается все-таки
с первого раза, но с большой задержкой. Или при попытке
повернуть телефон. Или потрясти.
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Для тех, кто сомневается, что это происходит в мейн треде:
• когда вы в последний раз попадали в didSelectRowAtIndexPath
в бэкграунде?
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main"
bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];
[self presentViewController: newController animated:YES completion:nil];
}
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Ладно, ставим брейкпоинт, запускаем дебаггер, видим, что
это действительно главный тред. Ой. С дебаггером все
стабильно работает.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main"
bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];
[self presentViewController: newController animated:YES completion:nil];
}
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Ок. Сделаем так. В таком варианте мы видим, что тред все-
таки главный, а приложение все-таки не работает.
• Кстати, вьюконтроллер показывается, когда переключается
минута на часах в статус баре. Или уменьшается процент
батарейки.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
NSLog(@"Thread: %@", [NSThread currentThread]);
UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main"
bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];
[self presentViewController: newController animated:YES completion:nil];
}
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Вот так все работает стабильно. Задача выполнена, но чувство
удовлетворения не достигнуто.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main"
bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];

dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:newController animated:YES completion:nil];
});
}
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Оказывается, это известный баг с версии чуть ли не 5.х,
который связан с тем, что ранлуп главного потока почему-то
засыпает, и просыпается только от внешнего события, либо от
явного пробуждения. Например, вот так:
CFRunLoopWakeUp(CFRunLoopGetCurrent());
ПРОСТО ИСТОРИЯ ТРИ
ИСТОРИЯ ТРИ
• Задача выполнена, чувство удовлетворения достигнуто.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main"
bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];
[self presentViewController: newController animated:YES completion:nil];
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• Error: Error Domain=AVFoundationErrorDomain Code=-11800
"The operation could not be completed"
UserInfo={NSUnderlyingError=0x60800004c210 {Error
Domain=NSOSStatusErrorDomain Code=-12780 "(null)"},
NSLocalizedFailureReason=An unknown error occurred (-12780),
NSLocalizedDescription=The operation could not be completed}
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• Дано: есть AVPlayer. Есть AVPlayerItem, в который положена
композиция. В композиции несколько треков. Если при
первом проигрывании композиции в каком-то из треков нет
данных, то потом этот трек не будет проигрываться никогда.
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• Может, надо добавить на неиспользуемый трек пустоты?
• Может, надо, чтобы все треки были одинаковой длины?
• Может, проблема в том, что у нас везде разные знаменатели у
CFTime? (зачем они тогда вообще нужны, если надо самим
следить за одинаковостью)
• Может, надо уже просто добавить ассет с тишиной и ставить
его в начало каждого трека просто потому что почему бы и
нет? (конечно нет)
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• НЕТ
• НЕТ
• НЕТ
• Да, даже вставление маленького звука в начало трека
работает через раз.
• Ничего не работает. Ну то есть как, все то работает, то не
работает. Режем лишнее, готовимся к Support Request.
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• А в мини-варианте все заработало.
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• Давайте посмотрим ближе
AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset: self.composition];
AVPlayer* player = [[AVPlayer alloc] initWithPlayerItem:item];
[player play];
КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ
ИСТОРИЯ ЧЕТЫРЕ
• Давайте посмотрим ближе
• Отличие в том, что в мини-приложении я сразу создаю и
AVPlayerItem, и AVPlayer, а в большом приложении я
пользуюсь одними и теми же. AVPlayer тут роли не играет, а
вот AVPlayerItem созданный на базе AVMutableComposition
надо пересоздавать каждый раз, когда композиция
изменяется.
AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset: self.composition];
AVPlayer* player = [[AVPlayer alloc] initWithPlayerItem:item];
[player play];
НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ
ИСТОРИЯ ПЯТЬ
• Дано: чужой проект, в котором после небольшого, казалось
бы, изменения, все перестало работать.
• Пока конфигурирование оформления ячеек производилось
локально, все было в порядке. Как только данные о стиле
ячейки стали приходить с сервера, все пошло прахом.
НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ
ИСТОРИЯ ПЯТЬ
• Дано: чужой проект, в котором после небольшого, казалось
бы, изменения, все перестало работать.
• Пока конфигурирование оформления ячеек производилось
локально, все было в порядке. Как только данные о стиле
ячейки стали приходить с сервера, все пошло прахом.
[self doSomeAutomaticEventStyleSetup];
self.eventStyle = jsonData[kEventStyleKey];
НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ
ИСТОРИЯ ПЯТЬ
if (dataObject.eventStyle == kDefaultStyleKey) {
//...
} else if (dataObject.eventStyle == kClassicStyleKey) {
//...
} else if (dataObject.eventStyle == kHighlightedStyleKey) {
//...
} //...
НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ
ИСТОРИЯ ПЯТЬ
NSString* literal = @"literal"; // 0x00001111
NSString* immutable1 = [NSString stringWithString: literal]; //0x00001111
NSString* immutable2 = [NSString stringWithString: immutable1]; //0x00001111
NSString* immutable3 = [NSString stringWithFormat: @"%@", literal]; // 0x00002222
NSString* immutable4 = [NSString stringWithFormat: @"%@", immutable3]; // 0x00002222
NSString* immutable5 = [NSString stringWithFormat: @"%@", immutable4]; // 0x00002222
NSString* mutable = [immutable5 mutableCopy]; //some random address
NSString* immutableAgain = [NSString stringWithString: mutable]; //0x00002222 again

Más contenido relacionado

La actualidad más candente

Коротко о React.js
Коротко о React.jsКоротко о React.js
Коротко о React.jsMad Devs
 
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScript
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScriptCSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScript
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScriptAlexey Ivanov
 
Многопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихМногопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихYandex
 
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...Ontico
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в DjangoMoscowDjango
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка DjangoVladimir Rudnyh
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерSergey Platonov
 
Филипп Ковалев — Путь в npm
Филипп Ковалев — Путь в npmФилипп Ковалев — Путь в npm
Филипп Ковалев — Путь в npmYandex
 
О чем мы забываем в QA или “Знакомьтесь – Manageability!”
О чем мы забываем в QA или “Знакомьтесь – Manageability!”О чем мы забываем в QA или “Знакомьтесь – Manageability!”
О чем мы забываем в QA или “Знакомьтесь – Manageability!”SQALab
 
ДАМП 2015 Екатеринбург
ДАМП 2015 ЕкатеринбургДАМП 2015 Екатеринбург
ДАМП 2015 ЕкатеринбургAlexey Ivanov
 
Memory managment in i os (1)
Memory managment in i os (1)Memory managment in i os (1)
Memory managment in i os (1)it-park
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentationIvan Filimonov
 
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...Ontico
 
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".Badoo Development
 
Selenium: начало работы
Selenium: начало работыSelenium: начало работы
Selenium: начало работыPaul Stashevsky
 
JavaScript Базовый. Занятие 07.
JavaScript Базовый. Занятие 07.JavaScript Базовый. Занятие 07.
JavaScript Базовый. Занятие 07.Igor Shkulipa
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseCocoaHeads
 

La actualidad más candente (19)

Коротко о React.js
Коротко о React.jsКоротко о React.js
Коротко о React.js
 
Асинхронный JavaScript
Асинхронный JavaScriptАсинхронный JavaScript
Асинхронный JavaScript
 
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScript
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScriptCSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScript
CSS-в-JS, HTML-в-JS, ВСЁ-в-JS. Все гораздо проще, когда всё вокруг JavaScript
 
Многопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин КрамлихМногопоточность в браузере. Модель акторов — Константин Крамлих
Многопоточность в браузере. Модель акторов — Константин Крамлих
 
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...
Радости и гадости регрессионного тестирования вёрстки / Алексей Малейков (HTM...
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка Django
 
Григорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптерГригорий Демченко, Универсальный адаптер
Григорий Демченко, Универсальный адаптер
 
Филипп Ковалев — Путь в npm
Филипп Ковалев — Путь в npmФилипп Ковалев — Путь в npm
Филипп Ковалев — Путь в npm
 
О чем мы забываем в QA или “Знакомьтесь – Manageability!”
О чем мы забываем в QA или “Знакомьтесь – Manageability!”О чем мы забываем в QA или “Знакомьтесь – Manageability!”
О чем мы забываем в QA или “Знакомьтесь – Manageability!”
 
ДАМП 2015 Екатеринбург
ДАМП 2015 ЕкатеринбургДАМП 2015 Екатеринбург
ДАМП 2015 Екатеринбург
 
Memory managment in i os (1)
Memory managment in i os (1)Memory managment in i os (1)
Memory managment in i os (1)
 
Java 8 puzzlers
Java 8 puzzlersJava 8 puzzlers
Java 8 puzzlers
 
FPUG Dzyga presentation
FPUG Dzyga presentationFPUG Dzyga presentation
FPUG Dzyga presentation
 
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...
То, что вы хотели знать о HandlerSocket, но не смогли нагуглить / Сергей Авер...
 
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
Доклад Сергея Аверина на CodeFest-2013. "MySQL+HandlerSocket=NoSQL".
 
Selenium: начало работы
Selenium: начало работыSelenium: начало работы
Selenium: начало работы
 
JavaScript Базовый. Занятие 07.
JavaScript Базовый. Занятие 07.JavaScript Базовый. Занятие 07.
JavaScript Базовый. Занятие 07.
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap Database
 

Similar a "Истории из жизни опытного iOS разработчика"— Игорь Чертенков

Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоStanfy
 
Алексей Андросов - Debugger: Отладка кода
Алексей Андросов - Debugger: Отладка кодаАлексей Андросов - Debugger: Отладка кода
Алексей Андросов - Debugger: Отладка кодаYandex
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Ivan Tsyganov
 
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)Ontico
 
Deployment to production with an unexpected load
Deployment to production with an unexpected loadDeployment to production with an unexpected load
Deployment to production with an unexpected loadGrid Dynamics
 
WinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаWinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаMikhail Shcherbakov
 
Fun with core graphics
Fun with core graphicsFun with core graphics
Fun with core graphicsSoftTechnics
 
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "IT Event
 
Михаил Щербаков "WinDbg сотоварищи"
Михаил Щербаков "WinDbg сотоварищи"Михаил Щербаков "WinDbg сотоварищи"
Михаил Щербаков "WinDbg сотоварищи"Mikhail Shcherbakov
 
Архитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьАрхитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьAndrey Bibichev
 
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...Positive Hack Days
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...Alexey Paznikov
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... кодеTatyanazaxarova
 
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...Andrey Karpov
 
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOS
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOSCodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOS
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOSCodeFest
 
Froglogic Squish
Froglogic Squish Froglogic Squish
Froglogic Squish SQALab
 

Similar a "Истории из жизни опытного iOS разработчика"— Игорь Чертенков (20)

Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел Тайкало
 
Алексей Андросов - Debugger: Отладка кода
Алексей Андросов - Debugger: Отладка кодаАлексей Андросов - Debugger: Отладка кода
Алексей Андросов - Debugger: Отладка кода
 
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?Moscow Python Conf 2016. Почему 100% покрытие это плохо?
Moscow Python Conf 2016. Почему 100% покрытие это плохо?
 
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)
Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)
 
Deployment to production with an unexpected load
Deployment to production with an unexpected loadDeployment to production with an unexpected load
Deployment to production with an unexpected load
 
WinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаWinDbg в руках .NET разработчика
WinDbg в руках .NET разработчика
 
Fun with core graphics
Fun with core graphicsFun with core graphics
Fun with core graphics
 
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
 
Михаил Щербаков "WinDbg сотоварищи"
Михаил Щербаков "WinDbg сотоварищи"Михаил Щербаков "WinDbg сотоварищи"
Михаил Щербаков "WinDbg сотоварищи"
 
Архитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьАрхитектура в Agile: слабая связность
Архитектура в Agile: слабая связность
 
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...
Positive Hack Days. Олексюк. Автоматический поиск уязвимостей в программах бе...
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 
Lec 1
Lec 1Lec 1
Lec 1
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... коде
 
2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure
 
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
 
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOS
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOSCodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOS
CodeFest 2011. Бусыгин Р. — Создание кастомных интерфейсов для iOS
 
Froglogic Squish
Froglogic Squish Froglogic Squish
Froglogic Squish
 

Más de Improve Group

Ig effective comm_skillpack_part1
Ig effective comm_skillpack_part1Ig effective comm_skillpack_part1
Ig effective comm_skillpack_part1Improve Group
 
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...Improve Group
 
"Все, что вы должны знать о deeplink’ax" — Стас Жуковский
"Все, что вы должны знать о deeplink’ax" — Стас Жуковский"Все, что вы должны знать о deeplink’ax" — Стас Жуковский
"Все, что вы должны знать о deeplink’ax" — Стас ЖуковскийImprove Group
 
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...Improve Group
 
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016Improve Group
 
Оруэлу и не снилось
Оруэлу и не снилосьОруэлу и не снилось
Оруэлу и не снилосьImprove Group
 
Предиктивная телефония
Предиктивная телефонияПредиктивная телефония
Предиктивная телефонияImprove Group
 
У истоков интернета вещей
У истоков интернета вещейУ истоков интернета вещей
У истоков интернета вещейImprove Group
 
Мобильное API: Серверные разработчики VS Мобильные
Мобильное API: Серверные разработчики VS МобильныеМобильное API: Серверные разработчики VS Мобильные
Мобильное API: Серверные разработчики VS МобильныеImprove Group
 
Bitcoin в законе
Bitcoin в законеBitcoin в законе
Bitcoin в законеImprove Group
 
Как бросить аутсорс и начать жить
Как бросить аутсорс и начать житьКак бросить аутсорс и начать жить
Как бросить аутсорс и начать житьImprove Group
 
SWIFT нужен ли он вам?
SWIFT нужен ли он вам?SWIFT нужен ли он вам?
SWIFT нужен ли он вам?Improve Group
 

Más de Improve Group (12)

Ig effective comm_skillpack_part1
Ig effective comm_skillpack_part1Ig effective comm_skillpack_part1
Ig effective comm_skillpack_part1
 
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...
Разработчикам надоело собирать, а QA ждать: Как CI решает эти проблемы"— Вова...
 
"Все, что вы должны знать о deeplink’ax" — Стас Жуковский
"Все, что вы должны знать о deeplink’ax" — Стас Жуковский"Все, что вы должны знать о deeplink’ax" — Стас Жуковский
"Все, что вы должны знать о deeplink’ax" — Стас Жуковский
 
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...
Хватит выдумывать! Как задействовать коллективный разум разработчиков для HR...
 
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016
Сам себе ассессмент - Вера Манохина для нАйТи ответ 2016
 
Оруэлу и не снилось
Оруэлу и не снилосьОруэлу и не снилось
Оруэлу и не снилось
 
Предиктивная телефония
Предиктивная телефонияПредиктивная телефония
Предиктивная телефония
 
У истоков интернета вещей
У истоков интернета вещейУ истоков интернета вещей
У истоков интернета вещей
 
Мобильное API: Серверные разработчики VS Мобильные
Мобильное API: Серверные разработчики VS МобильныеМобильное API: Серверные разработчики VS Мобильные
Мобильное API: Серверные разработчики VS Мобильные
 
Bitcoin в законе
Bitcoin в законеBitcoin в законе
Bitcoin в законе
 
Как бросить аутсорс и начать жить
Как бросить аутсорс и начать житьКак бросить аутсорс и начать жить
Как бросить аутсорс и начать жить
 
SWIFT нужен ли он вам?
SWIFT нужен ли он вам?SWIFT нужен ли он вам?
SWIFT нужен ли он вам?
 

"Истории из жизни опытного iOS разработчика"— Игорь Чертенков

  • 2. ИСТОРИЯ 1 Дано: у клиента стабильно повторяющийся краш, а у нас, конечно же, все работает. Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 0 Last Exception Backtrace: 0 CoreFoundation 0x18ef211b8 __exceptionPreprocess + 124 1 libobjc.A.dylib 0x18d95855c objc_exception_throw + 56 2 CoreFoundation 0x18edfc71c -[__NSArrayM objectAtIndex:] 3 BeautifulApp 0x10005ce4c -[BAWebSerivce ...]
  • 3. ИСТОРИЯ 1 Дано: у клиента стабильно повторяющийся краш, а у нас, конечно же, все работает. Источник горя по крашлогу:
 
 (но у нас-то все нормально) - (void) dataProvider:(id) dataProvider didFinishLoadingTasks: (NSArray<TFTask*>*) tasks activeTaskIndex: (NSInteger) activeIndex { //... self.tasks = tasks; self.activeTask = tasks[activeIndex]; //... }
  • 4. ИСТОРИЯ 1 Дано: у клиента стабильно повторяющийся краш, а у нас, конечно же, все работает. Тем временем, ПМ, поставивший версию с тестфлайта, воспроизводит баг с завидной регулярностью.
 Чем отличаются билд у разработчика на телефоне и билд у клиента в тестфлайте?
  • 5. ИСТОРИЯ 1 Дано: у клиента стабильно повторяющийся краш, а у нас, конечно же, все работает. Тем временем, ПМ, поставивший версию с тестфлайта, воспроизводит баг с завидной регулярностью.
 Build configuration: Debug vs. Release
  • 6. ИСТОРИЯ 1 И действительно, int index; //... if ([jsonData[kHasActiveSelection] boolValue]) { index = [jsonData[kSelectedItem] integerValue]; }
  • 7. ИСТОРИЯ 1 И действительно, int index; //... if ([jsonData[kHasActiveSelection] boolValue]) { index = [jsonData[kSelectedItem] integerValue]; } int index;
  • 8. ИСТОРИЯ 1 Сейчас таким свойством обладают только элементарные типы (и компилятор по-прежнему не предупреждает, если только это не SEL). То есть встретиться с этой проблемой можно только используя непроинициализированные индексы или не-ARC указатели (например, на CoreFoundation-объекты или просто void* буферы) Но до появления ARC эта проблема касалась в том числе и любых объектов, указатели на которые создавались на стеке, и очень легко было пострадать: NSError* error; [_managedObjectContext save: &error]; if (error) { NSLog(@"error: %@", error); }
  • 9. ИСТОРИЯ 1 А вообще различия между Debug и Release конфигурациями таят в себе много незабываемых и веселых моментов
  • 10. ИСТОРИЯ 1 А вообще различия между Debug и Release конфигурациями таят в себе много незабываемых и веселых моментов: NSAssert([[self managedObjectContext] save: nil], @"Error saving context");
  • 11. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • Дано: приложение сохраняет данные в папку документов, но при следующем запуске приложения сохраненные данные куда-то исчезают.
  • 12. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • Дано: приложение сохраняет данные в папку документов, но при следующем запуске приложения сохраненные данные куда-то исчезают. • Медиаданные хранятся в виде файлов, пути к файлам хранятся где-то неважно где (пусть хоть в NSUserDefaults)
  • 13. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • Дано: приложение сохраняет данные в папку документов, но при следующем запуске приложения сохраненные данные куда-то исчезают. • Медиаданные хранятся в виде файлов, пути к файлам хранятся где-то неважно где (пусть хоть в NSUserDefaults) • Ошибок при записи нет. Все корректно записывается. Ошибка при чтении есть: файл не найден. Как так не найден- то, когда вот только что он нормально записался и путь тот же самый?
  • 14. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • file:///Users/iosdeveloper/Library/Developer/CoreSimulator/ Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/ Containers/Data/Application/8AB0AAF5-483A-4A21- B503-86CFCE78C034/Documents/
  • 15. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • file:///Users/iosdeveloper/Library/Developer/CoreSimulator/ Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/ Containers/Data/Application/8AB0AAF5-483A-4A21- B503-86CFCE78C034/Documents/ • Снаряжаем экспедицию
  • 16. ОБЫЧНАЯ ИСТОРИЯ ДВА ИСТОРИЯ ДВА • file:///Users/iosdeveloper/Library/Developer/CoreSimulator/ Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/ Containers/Data/Application/8AB0AAF5-483A-4A21- B503-86CFCE78C034/Documents/ • Снаряжаем экспедицию • file:///Users/iosdeveloper/Library/Developer/CoreSimulator/ Devices/CAD31984-03BC-474C-9C3B-AAED8DBC2EF0/data/ Containers/Data/Application/8AB0AAF5-483A-4A21- B503-86CFCE78C034/Documents/
  • 17. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Дано: при клике на ячейку модальный вьюконтроллер стабильно показывается со второго раза.
  • 18. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Дано: при клике на ячейку модальный вьюконтроллер стабильно показывается со второго раза. • Удаляем все лишнее. Проблема сохраняется даже в проекте на 20 строчек.
  • 19. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Дано: при клике на ячейку модальный вьюконтроллер стабильно показывается со второго раза. • Удаляем все лишнее. Проблема сохраняется даже в проекте на 20 строчек. • Замечаем, что иногда вьюконтроллер показывается все-таки с первого раза, но с большой задержкой. Или при попытке повернуть телефон. Или потрясти.
  • 20. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Дано: при клике на ячейку модальный вьюконтроллер стабильно показывается со второго раза. • Удаляем все лишнее. Проблема сохраняется даже в проекте на 20 строчек. • Замечаем, что иногда вьюконтроллер показывается все-таки с первого раза, но с большой задержкой. Или при попытке повернуть телефон. Или потрясти.
  • 21. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Для тех, кто сомневается, что это происходит в мейн треде: • когда вы в последний раз попадали в didSelectRowAtIndexPath в бэкграунде? - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"]; [self presentViewController: newController animated:YES completion:nil]; }
  • 22. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Ладно, ставим брейкпоинт, запускаем дебаггер, видим, что это действительно главный тред. Ой. С дебаггером все стабильно работает. - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"]; [self presentViewController: newController animated:YES completion:nil]; }
  • 23. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Ок. Сделаем так. В таком варианте мы видим, что тред все- таки главный, а приложение все-таки не работает. • Кстати, вьюконтроллер показывается, когда переключается минута на часах в статус баре. Или уменьшается процент батарейки. - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"Thread: %@", [NSThread currentThread]); UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"]; [self presentViewController: newController animated:YES completion:nil]; }
  • 24. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Вот так все работает стабильно. Задача выполнена, но чувство удовлетворения не достигнуто. - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"];
 dispatch_async(dispatch_get_main_queue(), ^{ [self presentViewController:newController animated:YES completion:nil]; }); }
  • 25. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Оказывается, это известный баг с версии чуть ли не 5.х, который связан с тем, что ранлуп главного потока почему-то засыпает, и просыпается только от внешнего события, либо от явного пробуждения. Например, вот так: CFRunLoopWakeUp(CFRunLoopGetCurrent());
  • 26. ПРОСТО ИСТОРИЯ ТРИ ИСТОРИЯ ТРИ • Задача выполнена, чувство удовлетворения достигнуто. - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController* newController = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"Modal"]; [self presentViewController: newController animated:YES completion:nil]; CFRunLoopWakeUp(CFRunLoopGetCurrent()); }
  • 27. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ
  • 28. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • Error: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x60800004c210 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed}
  • 29. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • Дано: есть AVPlayer. Есть AVPlayerItem, в который положена композиция. В композиции несколько треков. Если при первом проигрывании композиции в каком-то из треков нет данных, то потом этот трек не будет проигрываться никогда.
  • 30. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • Может, надо добавить на неиспользуемый трек пустоты? • Может, надо, чтобы все треки были одинаковой длины? • Может, проблема в том, что у нас везде разные знаменатели у CFTime? (зачем они тогда вообще нужны, если надо самим следить за одинаковостью) • Может, надо уже просто добавить ассет с тишиной и ставить его в начало каждого трека просто потому что почему бы и нет? (конечно нет)
  • 31. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • НЕТ • НЕТ • НЕТ • Да, даже вставление маленького звука в начало трека работает через раз. • Ничего не работает. Ну то есть как, все то работает, то не работает. Режем лишнее, готовимся к Support Request.
  • 32. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • А в мини-варианте все заработало.
  • 33. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • Давайте посмотрим ближе AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset: self.composition]; AVPlayer* player = [[AVPlayer alloc] initWithPlayerItem:item]; [player play];
  • 34. КАК ТРИ, ТОЛЬКО ЧЕТЫРЕ ИСТОРИЯ ЧЕТЫРЕ • Давайте посмотрим ближе • Отличие в том, что в мини-приложении я сразу создаю и AVPlayerItem, и AVPlayer, а в большом приложении я пользуюсь одними и теми же. AVPlayer тут роли не играет, а вот AVPlayerItem созданный на базе AVMutableComposition надо пересоздавать каждый раз, когда композиция изменяется. AVPlayerItem* item = [[AVPlayerItem alloc] initWithAsset: self.composition]; AVPlayer* player = [[AVPlayer alloc] initWithPlayerItem:item]; [player play];
  • 35. НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ ИСТОРИЯ ПЯТЬ • Дано: чужой проект, в котором после небольшого, казалось бы, изменения, все перестало работать. • Пока конфигурирование оформления ячеек производилось локально, все было в порядке. Как только данные о стиле ячейки стали приходить с сервера, все пошло прахом.
  • 36. НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ ИСТОРИЯ ПЯТЬ • Дано: чужой проект, в котором после небольшого, казалось бы, изменения, все перестало работать. • Пока конфигурирование оформления ячеек производилось локально, все было в порядке. Как только данные о стиле ячейки стали приходить с сервера, все пошло прахом. [self doSomeAutomaticEventStyleSetup]; self.eventStyle = jsonData[kEventStyleKey];
  • 37. НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ ИСТОРИЯ ПЯТЬ if (dataObject.eventStyle == kDefaultStyleKey) { //... } else if (dataObject.eventStyle == kClassicStyleKey) { //... } else if (dataObject.eventStyle == kHighlightedStyleKey) { //... } //...
  • 38. НЕ СОВСЕМ ИСТОРИЯ ДАЖЕ ИСТОРИЯ ПЯТЬ NSString* literal = @"literal"; // 0x00001111 NSString* immutable1 = [NSString stringWithString: literal]; //0x00001111 NSString* immutable2 = [NSString stringWithString: immutable1]; //0x00001111 NSString* immutable3 = [NSString stringWithFormat: @"%@", literal]; // 0x00002222 NSString* immutable4 = [NSString stringWithFormat: @"%@", immutable3]; // 0x00002222 NSString* immutable5 = [NSString stringWithFormat: @"%@", immutable4]; // 0x00002222 NSString* mutable = [immutable5 mutableCopy]; //some random address NSString* immutableAgain = [NSString stringWithString: mutable]; //0x00002222 again