Что нового в Objective-C и Foundation принесла iOS 7

в 19:37, , рубрики: ios Objective-C, разработка под iOS

Что нового в Objective-C и Foundation принесла iOS 7

Objective-C является наиболее распространенным языком для разработки iOS и OS X приложений. Конечно, можно использовать сторонние фреймворки, которые позволяют разрабатывать приложения с использованием других языков, таких как HTML и JavaScript или C#, но если вы хотите написать невероятно быстро, эффективные приложения, то вам нужно использовать Objective-C.

Foundation является одним из основных структур, которые вы будете использовать при разработке приложений на языке Objective-C.

Как IOS разработчик, Вы должны быть в курсе актуальных и последних достижений в Objective-C и Foundation, в IOS 7 есть некоторые важные изменения которые нужно знать.

В этой статье мы сделаем краткий обзор некоторых новых функций в Objective-C и Foundation.

Давайте начнем!

Модули / Modules

Велика вероятность, что вы писали #import тысячу раз и более:

#import <UIKit/UIKit.h><br>
#import <MapKit/MapKit.h><br>
#import <iAd/iAd.h><br>

Этот синтаксис возвращает нас к корням 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, но модули также решают эту проблему аккуратно. (Есть ли что-нибудь, что не могут сделать модули? )

Модуль сообщает компилятору не только, какие заголовочные файлы составляют модуль, но также и что потребности быть соединенными. Это оберегает от необходимости вручную линковать 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 SDK!

Одним из главных усовершенствований в этой версии 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 lastObjec

NSData

Data — одна из тех вещей, с которыми Вы имеете дело каждый день, когда программируете. NSData — Фундаментальный класс, который инкапсулирует необработанные байты и обеспечивает методы для того, чтобы Вы управляли этими байтами, также читая и записывали данные в файл. Но одной очень общей задачей, для которой не было никакой собственной реализации, является кодирование и декодирование Base64. По крайней мере, это имело место до выпуска iOS 7

Base64 — группа схем кодирования двоичного файла к тексту, которые представляют двоичных данных в формате ASCII. Эти схемы обычно используются, где есть потребность закодировать двоичных данных, которые будут сохранены на или переданы по носителям, разработанным, чтобы иметь дело исключительно с текстовыми данными. Это удостоверилось, что данные остаются неповрежденными без модификации во время транспорта. Наиболее популярный способ использования кодирования Base64 обрабатывает присоединения в электронном письме, а также кодирует маленькие изображения, которые являются частью ответа JSON, возвращенного веб-API.

До iOS 7.0, Base64 кодирующие и декодирующие задачи потребовали, чтобы реализовали свой собственный метод или включали часть сторонней платформы. Типичным способом Apple это теперь очень удобно эта функциональность. Следующим образом есть четыре базовых метода 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. Это — довольно устойчивая платформа, главным образом потому что новые базовые классы не требуются слишком часто. Однако, 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 если это необходимо.

p.s. Попрошу строго не судить, если возможно то замечания пишите в личку, подправим. Спасибо за понимание.

Автор: yarmolchuk

Источник

Поделиться

* - обязательные к заполнению поля