Foundation является одним из основных структур, которые Вы будете использовать при разработке приложений на языке Objective-C.
Как IOS разработчик, Вы должны быть в курсе актуальных и последних достижений в Objective-C и Foundation, в IOS 7 есть некоторые важные изменения которые нужно знать.
В этой статье мы сделаем краткий обзор некоторых новых функций в Objective-C и Foundation.
Обратите внимание, что это статья, а не учебник — вы получите быстрый интенсивный курс, узнаете что появилось нового (вместе с некоторыми фрагментами кода).
Давайте начнем!
Модули
Велика вероятность, что вы писали #import тысячу раз и более:
#import <UIKit/UIKit.h> #import <MapKit/MapKit.h> #import <iAd/iAd.h>
Этот синтаксис возвращает нас к корням ObjectiveC: а именно к языку C. Оглавление предварительной обработки директивы #import, работает таким же образом, как #include. Разница лишь в том, что #import в отличии от #include не импортирует заголовки повторно, это одноразовая операция.
Когда препроцессор встречает директиву #import, он буквально заменяет одну строку кода всем содержимым заголовка импортируемого файла. Это делается рекурсивно, через потенциально большое количество заголовочных файлов.
Заголовочный файл UIKit, UIKit.h импортирует все другие заголовки, которые включенные в UIKit framework. Это означает, что Вы не должны вручную импортировать каждый заголовочный файл в framework, такие как UIViewController.h, UIView.h или UIButton.h.
Вас заинтересовал размер UIKit framework? Просмотрите и посчитайте все строки кода во всех заголовках UIKit, и вы увидите, что это составляет более 11 000 строк кода!
В стандартном iOS приложении Вы импортируете UIKit в большинство своих файлов, это означает, что каждый файл будет на 11,000 строк кода больше. Это совсем не идеал для приложения; больше строк кода означает более длительное время компиляции приложения.
Оригинальное решение: Предварительно скомпилированные заголовки.
Прекомпилированные файлы заголовков, или PCH-файлы, попытка решения этой проблемы путем обеспечения механизма для предварительного вычисления и кэширования большой части работы, требуемой во время фазы предварительной обработки компиляции. Вы, наверное, видели файл PCH, создаваемый с помощью шаблонов в Xcode, он выглядит следующим образом:
#import <Availability.h> #ifndef __IPHONE_5_0 #warning "This project uses features only available in iOS SDK 5.0 and later." #endif #ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #endif
#warning в данном случае уведомляет разработчика, что приложение, которое он создает, предназначается для SDK до IOS 5. UIKit и заголовочные файлы Foundation — часть этого запаса PCH, так как каждый файл в Вашем приложении будет использовать Foundation, и большинство будут использовать UIKit. Поэтому, это хорошие дополнения к PCH, так что предварительное вычисление и кэширование принесут пользу для компиляции каждого файла в Вашем приложении.
«Ну и что с этим не так?» Вы могли бы спросить. С технической точки зрения нет никакой ошибки в PCH, тоесть если он работает — не лезьте в него. Однако, вы можете упустить множество преимуществ в производительности, которые являются результатом улучшения и настройки PCH файла. Например, если несколько областей вашего приложения используют Map Kit framework, Вы можете увидеть улучшение по времени компиляции, просто добавив заголовок файла Map Kit или отдельных файлов заголовка классов Map Kit которые используете в свой PCH файл.
Мы все знаем что разработчики ленивы, хотя, и никто не имеет времени, чтобы настроить свой PCH файл для каждого проекта. Именно поэтому модули были разработаны как функция LLVM.
Примечание: LLVM — набор модульного и допускающего повторное использование компилятора и toolchain технологий, связанных Xcode. У LLVM есть несколько компонентов; наиболее важными для Objective-C разработчика это Clang, собственный C/C++ и Objective C компилятор; а также LLDB — собственный отладчик, который является лучшим другом разработчика.
Новое решение: Модули
Первое публичное появление модулей в Objective-C было в докладе, сделанным Doug Gregor от Apple в 2012 году на собрании LLVM разработчиков. Это увлекательный разговор, и он настоятельно рекомендуется для всех, кто интересуется разработкой своих компиляторов. Вы можете найти видео сессии в Интернете по адресу llvm.org/devmtg/2012-11/#talk6.
Модули инкапсуляции структур во многих способах чище, чем когда-либо прежде. Больше препроцессору не необходимо заменять #import директивы со всем содержимым файла. Вместо этого, модуль обертывает framework в автономный блок, который предварительно составлен таким же образом, как PCH файл и обеспечивает такое же улучшение скорости компиляции. Тем не менее, Вам больше не приходится констатировать framework который используете в файл PCH, Вы получите увеличение скорости просто с помощью модулей.
Но есть больше к модулям, чем просто это. Я уверен, что вы вспомните многочисленные шаги которые проходите, когда в первый раз используете новый framework для приложения, это имеет тенденцию проходить примерно так:
Добавьте строку #import используемого framework.
- Написать код, который использует framework.
- Скомпилировать.
- Посмотреть, поскольку ошибки появились во время линковки.
- Помнить, что Вы забыли слинковать framework.
- Добавить framework к фазе сборки проекта.
- Снова скомпилировать.
Невероятно распространено забыть слинковать framework, но модули решают эту проблему. (Есть ли что-нибудь, что не могут сделать модули? )
Модуль сообщает компилятору не только, какие заголовочные файлы составляtn модуль, но также и что потребности быть слинкован. Это оберегает от необходимости вручную линковать framework самостоятельно. Это — только маленькая вещь, но что-либо, что заставляет разработчика разработывать проще, является хорошей вещью!
Как использовать модули.
Модули чрезвычайно удобны в Ваших проектах. Для существующих проектов первое, что нужно сделать, включить их. Вы можете найти эту опцию, в вкладке modules в Build Settings Вашего проекта (выберите All, not Basic) и затем измините опцию Enable Modules на YES, так как показанно ниже:
Все новые проекты, созданные в Xcode 5, эта опцыя включена по умолчанию, но вы должны обязательно включить ее во всех существующих проектах.
Link Frameworks Automatically эта опция может быть использована, чтобы включить или отключить автоматическое связывание фреймворков, как это описано ранее. Есть мало причин, почему бы Вы хотели отключить эту возможность.
Как только модули включены, Вы можете начать использовать их в своем коде. Довально просто сделать это, однако есть одно небольшое изменение в синтаксисе к которому Вы так привыкли. Вместо обычного синтаксиса # import, вы просто используете import:
@import UIKit; @import MapKit; @import iAd;
Все еще возможно импортировать некоторые framework, которой Вам необходимы. Как пример, если бы Вам нужно было импортировать UIView, написали бы так:
@import UIKit.UIView;
Да — это действительно так просто! Ну, извините, это не совсем правда. Это даже проще. Технически, Вам не нужно конвертировать все ваши строки #import в import строки, так как компилятор неявно преобразует их для вас под капотом. Однако, это всегда — хорошая практика, чтобы начать использовать новый синтаксис так часто, как можете.
Прежде, чем начнете приходить в восторг, посмотрите на негативная сторону модулей. С Xcode 5.0, Вы не сможете использовать модули с Вашими собственными frameworks или сторонними frameworks. Это, вероятно, придет в свое время — но сейчас это негативная сторона. Ничто не совершенно – даже Objective C!
Новый возращаемый тип – instancetype
Новый тип был добавлен в Objective-C, который назвали — instancetype. Его можно только использоваться в качестве возращаемого типа метода Objective-C, так же он используется в качестве подсказки для компилятора, что возращаемый тип метода будет экземпляром класса, которому принадлежит этот метод.
Примечание: Эта функция не является строго новой для Xcode 5 и IOS 7, но была украдкой брошена в недавние сборки Clang. Тем не менее, в Xcode 5 отмечана в первый раз, что Apple начал использовать его на протяжении всех frameworks. Вы можете узнать больше об этой возможности на официальном сайте Clang: clang.llvm.org/docs/LanguageExtensions.html # Objective-C-функциями.
Почему возращаемый тип instancetype полезный? Рассмотрите следующий пример кода:
NSDictionary *d = [NSArray arrayWithObjects:@(1), @(2), nil]; NSLog(@"%i", d.count);
Хотя это явно неправильно, компилятор не будет исторически делают абсолютно ничего, чтобы сообщить Вам об ошибке. Попробуйте сами, если у Вас установлен Xcode 4.6. Вы заметите, что никакого предупреждения Вы не получите, хотя в коде явно написанно не верно! Код будет работать даже без замечаний, так как и NSDictionary и экземпляры NSArray выведут количество елементов.
Причина таких действий во время выполнения благодаря мощному, динамическому характеру Objective-C. Тип является чисто руководством компилятора. Метод count ищется во время выполнения в любом классе. В этом случае метод count существует, так что компилятор считает, что все будет хорошо. Однако, это может вернуться, чтобы укусить Вас позже, если Вы добавили код, который использует другой метод, который NSArray не имеют общего с NSDictionary, например такой как objectAtIndex:. Поначалу не будет понятно, где именно скрылась проблема.
Но почему компилятор не выяснить, какой экземпляр возвращается + [arrayWithObjects NSArray:] и что он не является экземпляром класса NSDictionary? Ну, это потому, что сигнатура метода заключается в следующем:
+ (id)arrayWithObjects:(id)firstObj, ...;
Обратите внимание на тип возвращаемого значения это ID. Тип ID это значение любой Objective-C класс, он не должны быть даже подклассом NSObject. Он в буквальном смысле не имеет информации о типе, кроме того, это экземпляр Objective-C класса. Для этого, чтобы быть полезным, компилятор выдает предупреждения, когда Вы неявно приводите к ID для конкретного типа, например NSDictionary * в приведенном выше примере. Если бы такое выдавало предупреждение, идентификатор типа id был бы в значительной степени бесполезным.
Но почему возвращаемый тип метода в первую очередь ID? Это чтобы Вы могли успешно до сих пор использовать его без проблем. Чтобы продемонстрировать, почему так, рассмотрим следующий подкласс класса NSArray:
@interface MyArray : NSArray @end
Теперь рассмотрим использование вашего нового подкласса в коде ниже:
MyArray *array = [MyArray arrayWithObjects:@(1), @(2), nil];
Ах — теперь вы видите, почему возвращаемый тип arrayWithObjects: должен быть ID. Если бы это был NSArray *, то подкласс потребует, быть приведен к необходимому классу. Это где новый возвращаемый тип instancetype приходит на помощь.
Если вы посмотрите на файл заголовка для NSArray в IOS 7.0 SDK, вы заметите, сигнатуры метода для этого метода изменилось на следующую:
+ (instancetype)arrayWithObjects:(id)firstObj, ...;
Единственная разница — возращаемый тип. Этот новый возращаемый тип который обеспечивает подсказку для компилятора, что возвращаемое значение будет экземпляром класса, который метод вызываю. Таким образом, когда arrayWithObjects: вернет NSArray, возращеемый тип будет NSArray *; если вызвать такой же метод для MyArray, возращемый тип будет MyArray * и т.д.
С идентификатором типа id все отлично работает. Но если Вы скомпилируете Ваш исходный код в Xcode 5, теперь Вы увидите следующее предупреждение:
warning: incompatible pointer types initializing 'NSDictionary *' with an expression of type 'NSArray *' [-Wincompatible-pointer-types] NSDictionary *d = [NSArray arrayWithObjects:@(1), @(2), nil]; ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
w00t — теперь это полезно! Теперь Вы имеете возможность решить проблему прежде, чем это превратится в фатальную ошибку в Вашем приложение.
Инициализаторы — также кандидаты на использование этого нового возращаемого типа. Компилятор предуприждал Вас в течение некоторого времени, теперь если устанавливаете тип возврата инициализатора к тому из несовместимого типа. Но по-видимому это просто неявно преобразовывает возращаемый тип идентификатора в instancetype под капотом. Вы должны использовать instancetype для инициализаторов, потому что лучше использовать явное.
Стремитесь использовать instancetype как можно больше; это стало стандартом для Apple — и никогда не знаете, когда это сохранит Вам время для отладки.
Новый Foundations
Оставшаяся часть этой статьи посвящена различным новым частям и функциям в Foundation, базовая основа всех Objective-C разработчиков. Трудно разрабатывать приложений на Objective-C без Foundation, так как все IOS приложения требуют ее использовать.
Одним из главных усовершенствований в этой версии Foundation заключается в сетях; так много, на самом деле, что есть целая глава в учебнику по IOS 7, посвященная им. Это очень важна часть, так что мы собираемся позже выпустить сокращенный вариант главы из учебника IOS 7 Feast, чтобы помочь всем быть вкурсе новых добавленых возможностей.
Далее в этом разделе описываются другие интересные дополнения и изменения в Foundation.
NSArray
Попытка получить объект экземпляра NSArray выдаст исключение, если индекс, который будет вне длины массива. Будете также часто находить потребность получить доступ к первым и последним объектам массива, когда используете непостоянный массив. Первым прибыл — первым убыл (FIFO) очереди выталкиваете объекты с передней стороны массива, и в первым пришел — последним вышел (FILO) выталкиваете объекты с конца массива.
Однако, когда получаете первые или последние объекты с массива, Вы должны всегда удостовериться, что не переходите к чтению мимо конца массива, это может легко произойти, если массив был пуст. Это приводит к большому количеству ненужного повторяющего кода, чтобы обеспечить вызов метода objectAtIndex: не будет выводить исключение:
NSMutableArray *queue = [NSMutableArray new]; // ... if (queue.count > 0) { id firstObject = [queue objectAtIndex:0]; // Use firstObject } // ... if (queue.count > 0) { id lastObject = [queue objectAtIndex:(queue.count - 1)]; // Use lastObject }
В случае если Вы хотите получить последний объекта массива, Вы можете вызвать следующий метод NSArray:
- (id)lastObject;
Каждый в мире разработчики Objective-C — не сомненно будет рад, что впервые у них есть доступ к эквивалентному методу, чтобы получить первый объект массива:
- (id)firstObject;
Этот единый простой метод оказывается чрезвычайно полезным. Вам больше не нужно составлять массив, который являющийся пустым. Если Вы когда-либо сталкивались с падениями из-за того что перешли границу массива, то Вы обязательно полюбите это приятное дополнение.
Примечание: Если вы внимательно посмотрите на заголовок NSArray Вы увидите, что на самом деле метод firstObject был примерно с IOS 4.0, но он не был опубликован аж до IOS 7. Поэтому Вы могли бы получить метод до IOS 7, но это потребовало объявить селектор для firstObject в одном из Ваших собственных файлов заголовка, чтобы сообщить компилятору, что он на самом деле существует. Такой подход конечно не рекомендуется, так что это хорошо, что Apple, наконец принес его из сокрытия.
Предыдущий фрагменте кода теперь может быть переписан с использованием этих двух методов и Вы можете отказаться от ненужной проверки, как показано ниже:
NSMutableArray *queue = [NSMutableArray new]; // ... id firstObject = [queue firstObject]; // Use firstObject id lastObject = [queue lastObject]; // Use lastObject
NSData
Data — одна из тех вещей, с которыми Вы имеете дело каждый день, когда программируете. NSData — Фундаментальный класс, который инкапсулирует необработанные байты и обеспечивает методы для того, чтобы Вы управляли этими байтами, также читая и записывали данные в файл. Но одной очень общей задачей, для которой не было никакой собственной реализации, является кодирование и декодирование Base64. По крайней мере, это имело место до выпуска iOS 7
Base64 — группа схем кодирования двоичного файла к тексту, которые представляют двоичные данные в формате ASCII. Эти схемы обычно используются, когда есть необходимость закодировать двоичные данные, которые будут сохранены или записанные на носитель, разработанным, чтобы иметь дело исключительно с текстовыми данными. Удостовериться, что данные остаются неповрежденными без модификации во время перемещения. Наиболее популярный способ использования кодирования Base64, эта система широко используется в электронной почте для представления бинарных файлов в тексте письма, а также кодирует маленькие изображения, которые являются частью ответа в формате JSON, возвращенного веб-API.
До iOS 7.0, в Base64 кодирующие и декодирующие задачи потребовали, чтобы разработчики реализовали свой собственные методы или использовать стороние framework. Теперь очень легко использовать эту функциональность, нам предоставляюю четыре базовых метода Base64:
- (id)initWithBase64EncodedString:(NSString *)base64String options:(NSDataBase64DecodingOptions)options; - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options; - (id)initWithBase64EncodedData:(NSData *)base64Data options:(NSDataBase64DecodingOptions)options; - (NSData *)base64EncodedDataWithOptions:(NSDataBase64EncodingOptions)options;
Первые два метода работают со строками, в то время как последние два имеют дело с закодированными данными UTF-8. Обе пары методов выполняют то же действие, но иногда использование того или иного метода окажется более эффективным. Если Вы кодируету строку в Base64 и затем записываете ее в файл, Вы можете решить использовать пару, которая обрабатывает закодированные данные UTF-8. С другой стороны, Если Вы кодируету строку в Base64 и затем используете ее в некотором JSON, можете решить использовать пару методов, которая обрабатывает строки.
Таким образом, если когда-либо использовали стороние методы для Base64 в своем проекте, теперь настало время, чтобы удалить тот ненужный код и использовать реализацию Apple вместо своего кода!
NSTimer
Таймеры часто находят использование в приложения, которые выполняют периодические задачи. Столь полезный, как они могут быть, только проблема состоит в том, что они могут постоянно использоваться, и когда несколько таймеров используются. Это означает, что ЦП постоянно активен; было бы намного более эффективно, если бы ЦП проснулся, выполнил пакет задач и затем вернулся ко сну. Чтобы решить эту проблему, Apple добавил свойство допуска к NSTimer, чтобы помочь приспособиться к этому поведение.
Свойство допуска предоставляет системе руководство относительно того, как поздно таймеру разрешают срабатывать после описания расписания. Базовая система тогда будет групировтаь задачи соответственно, чтобы уменьшить издержки ЦП. Методы для того, чтобы получить доступ к этому новому свойству следующие:
- (NSTimeInterval)tolerance; - (void)setTolerance:(NSTimeInterval)tolerance;
Можете прочитать, что Вы никогда не должны использовать это свойство, но если запускаете несколько таймеров в очень близкую последовательность, можете счесть полезным протестировать использования ЦП Вашего приложения в сравнении с эталоном, используя Instruments при изменений этой настройки.
NSProgress
Не так часто, случается что полностью новые классы были добавлены к Foundation. Это — довольно стабильный framework, главным образом потому что новые базовые классы не требуются слишком часто. Однако, iOS 7.0 приносит полностью новый класс под названием NSProgress.
В основном NSProgress стремится обеспечивать прогресс, сообщающий всюду по коду Objective-C, аккуратно разделяя прогресс отдельных компонентов. Например, если выполняете несколько различных задач для некоторых данных, тогда каждая задача может контролировать свой собственный прогресс и сообщить его родительской задаче.
Структура NSProgress
Самый простой способ использовать NSProgress состоит в том, чтобы использовать его, для того что бы сообщить о достижениях по ряду задач. Например, если Вы имеете 10 завершонных задач, затем можете сообщить о прогрессе, поскольку каждая задача завершина. Поскольку каждая задача завершенна, прогресс восстанавливает работоспособность на 10%. Затем используя Key Value Observing (KVO) экземпляра NSProgress, будете уведомлены об этом происходящем увеличении. Могли бы использовать это уведомление в качестве возможности обновить индикатор выполнения или установить текст на метке, чтобы показать информацию относительно прогресса.
Но это еще не все что нам дает NSProgress. Apple сделал его невероятно мощным, главным образом с помощью дочерней родительской структуры отношения. Структура NSProgress во многом как вложенное дерево: у каждого экземпляра могут быть один родитель и много дочерних элементов. У каждого экземпляра есть общее количество единиц задач, которые будут выполняться, и в то время как задача прогрессирует, завершенное число модулей обновлено, чтобы отобразить текущее состояние. При этом родитель (если существуете) также уведомлен относительно прогресса.
Для уменьшения необходимости распространять экземпляры NSProgress, у каждого потока есть свой собственный экземпляр NSProgress, и дочерние экземпляры могут быть созданы непосредственно из этого экземпляра. Без этой функциональности каждая задача, которая требовала сообщить о прогрессе таким образом, должна будет быть изменена, чтобы взять параметр NSProgress.
Создание отчетов о прогрессе
Использование NSProgress просто. Все начинается со следующего метода:
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount;
Это создает новый экземпляр NSProgress как дочерний элемент текущего экземпляра и инициализирует его с общим количеством единиц работы, которые будут выполняться в целом. Например, если бы задача состояла в том, чтобы циклично пройти через массив, то, вероятно, инициализировали бы экземпляр NSProgress с количеством массива, так:
NSArray *array = /* ... */; NSProgress *progress = [NSProgress progressWithTotalUnitCount:array.count]; [array enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { // Perform an expensive operation on obj progress.completedUnitCount = idx; }];
В то время как итерация прогрессирует, вышеупомянутый код обновляет экземпляр NSProgress, чтобы отразить текущий прогресс.
Получение обновления прогресса
Вы можете определить прогресс задачи в любой точке, использовая следующее свойство:
@property (readonly) double fractionCompleted;
Это возвращает значение от 0 до 1, указывая общий прогресс задачи. Когда нет никаких дочерних экземпляров в игре, fractionCompleted — завершенное количество модулей, разделенное на общее количество модулей.
Key Value Observing (KVO) — лучший способ, который будет уведомлять, когда свойство fractionCompleted изменяет свое значение. Использование так просто. Все, что Вы должны сделать, это зарегистрироваться как наблюдатель свойства fractionCompleted соответствующего объекта NSProgress:
[_progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:NULL];
Затем переопределите метод для использование KVO, чтобы уведомить относительно изменений:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == _progress) { // Handle new fractionCompleted value return; } // Always call super, incase it uses KVO also [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; }
В этом методе обработали бы изменение значения fractionCompleted. Например, могли бы изменить значение индикатора выполнения или метки, чтобы указать текущий уровень завершения.
Конечно, это важно помнить, чтобы удалить из KVO как только Вы закончите:
[_progress removeObserver:self forKeyPath:@"fractionCompleted" context:NULL];
Вы всегда должны отменить регистрацию в приложение или приложение “рухнет”, если не отмените регистрацию, зарегистрированного объекта (Self в данном примере), будет освобожден. Поэтому необходимо убедиться, что Вы отменили регистрацию в качестве последнего средства в dealloc если это необходимо.
ссылка на оригинал статьи http://habrahabr.ru/post/198226/
Добавить комментарий