От переводчика: В свете последних событий быть разработчиком на Objective-С стало уже не так модно, некоторые уже бегут сдавать его в утиль, но я считаю, что это делать еще рановато, потому позволю себе поделиться переводом. Начав читать оригинал, не ждал ничего нового в, казалось бы, простой и очевидной теме, но в итоге вынес достаточно полезных моментов.
Существуют, давайте их назовем «одномерными», типы данных: числа, строки, содержащие в себе по сути несколько значений, которые могут быть извлечены при помощи математических действий или простым парсингом. В качестве примера можно привести шестнадцатеричную запись цвета #EE8262, которая содержит компоненты для красного, синего и зеленого цвета, или, например, регулярное выражение, которое при помощи пары символов повзоляет искать сложные подстроки.
Среди всех «одномерных» типов данных безусловным победителем является URI. Посудите сами, здесь в одной человекочитаемой строчке находится достаточно данных, чтобы понять где находится та или иная информация, которая была, есть или когда-либо будет на компьютере.
Обычно URI включает в себя следующие компоненты: схему, некоторую иерархическую часть, запрос и фрагмент:
<scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]
Многие протоколы, как например HTTP, описывают некоторую заданную структуру такую как имя пользователя, пароль, порт и путь в иерархической части:
foo:// | username: | password | @ example.com | :8042 | /over/there/index | .dtb | ?type=animal&name=narwhal | #nose |
scheme | username | password | hostname | port | path | extension | query | fragment |
Для уверенной работы с сетевым взаимодействием необходимо хорошее знание URL компонентов. Для программиста это означает свободное использование URI компонентов стандартной библиотеки.
В Foundation URL'ы представлены классом NSURL.
NSURL обычно инстанцируется при помощи классового метода URLWithString:
NSURL *url = [NSURL URLWithString:@"http://example.com"];
Если строка, которую отдаем, является не валидным URL, то получим nil.
NSString имеет некий рудиментарный функционал для манипуляций с путями, об этом можно почитать здесь (там в том числе рассказывается про то, что Apple советует переходить от NSString к NSURL-like API для таких классов как NSFileManager). К сожалению, миграция от NSString к NSURL не идет так гладко, как хотелось бы. Hecмотря на то, что преобразование из NSString в NSURL не самый удобный шаг, это — правильно. Если значение — URL, оно должно храниться и передаваться как NSURL; использование класса не по назначению — признак лени и пример плохого дизайна API.
Кстати, что думаете насчет использования @@ в качестве литерала для NSURL (т.е. @@«example.com»)?
Также у NSURL есть классовый метод +URLWithString:relativeToURL:, который используется для создания URL из строки относительного некоторого базового URL. Поведение этого метода может быть не всегда очевидным в случае, когда относительный путь кончается на `/`
Вот несколько примеров, которые покажут как этот метод работает:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL];
// http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL];
// http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL];
// http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL];
// http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL];
// http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL];
// http://example2.com/
URL Components
NSURL предоставляет методы доступа ко всем URL компонентам, описанным в RFC 2396:
- absoluteString
- absoluteURL
- baseURL
- fileSystemRepresentation
- fragment
- host
- lastPathComponent
- parameterString
- password
- path
- pathComponents
- pathExtension
- port
- query
- relativePath
- relativeString
- resourceSpecifier
- scheme
- standardizedURL
- user
Документацию и примеры можно посмотреть в докуменентации — это неплохой способ познакомиться со всеми компонентами.
Отдельно надо сказать, что хоть имя пользователя и пароль можно хранить в URL, лучше использовать NSURLCreditnal или хранить их в связке ключей.
NSURLComponents
NSURLComponents появились в iOS 7 и Mac OS 10.9, честно говоря, лучшим названием для этого класса было бы NSMutableURL. Из-за отсутствия документации, этот класс стал одним из самых «секретных» дополнений в Foundation.
Инстансы NSURLComponents создаются при помощи:(+componentsWithString: и +componentsWithURL:resolvingAgainstBaseURL:). Само собой можно просто послать alloc и init, без каких либо аргументов, чтобы создать пустой контейнер.
Различие между NSURL и NSURLComponents в том, что у последнего свойства изменяемые. Это предоставляет безопасный и простой способ изменить отдельные компоненты в URL:
- scheme
- user
- password
- host
- port
- path
- query
- fragment
Попытка установить неправильную схему или выбрать отрицательное число для порта закончится исключением.
Также у NSURLComponents есть изменяемые percent-encoded варианты каждого компонента.
- percentEncodedUser
- percentEncodedPassword
- percentEncodedHost
- percentEncodedPath
- percentEncodedQuery
- percentEncodedFragment
При получении этих свойств мы получим их в percent-encoded виде. Изменяя эти свойства нужно отдавать уже заэнкоженную строчку, иначе — исключение. `;` — является корректным символом, но рекомендуется его также энкодить для лучшей своместимости с NSURL. (-stringByAddingPercentEncodingWithAllowedCharacters: заэнкодит все `;` если передать URLPathAllowedCharacterSet)
Percent-Encoding
NSURL — toll-free bridged для CFURLRef. Последний является низкоуровневым API на С, который эффективно повторяет функциональность NSURL, но за исключением CFURLCreateStringByAddingPercentEscapes и CFURLCreateStringByReplacingPercentEscapesUsingEncoding:
— CFURLCreateStringByAddingPercentEscapes: создает копию строчки, заменяя конкретные символы на соответствующие им заэскейпенные последовательности в соответствующей кодировке.
CFStringRef CFURLCreateStringByAddingPercentEscapes (
CFAllocatorRef allocator,
CFStringRef originalString,
CFStringRef charactersToLeaveUnescaped,
CFStringRef legalURLCharactersToBeEscaped,
CFStringEncoding encoding
);
— CFURLCreateStringByReplacingPercentEscapesUsingEncoding: делает все наоборот заменяет заэскейпенные последовательности на соответствующие символы.
CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding (
CFAllocatorRef allocator,
CFStringRef origString,
CFStringRef charsToLeaveEscaped,
CFStringEncoding encoding
);
URL для закладок
Последний вопрос, который хотелось бы обсудить — URL'ы для закладок, это те пути, которые можно использовать, чтобы безопасно ссылаться на файлы между запусками приложения. Можно думать о них как о файловых дескрипторах.
Закладка — закрытая файловая структура, которая заключена в объект NSData, она описывает местоположене файла. В то время как путь и ссылочный URL потенциально ненадежны, закладка может быть использована чтобы воссоздать URL, если даже файл был перемещен или переименован.
Больше про ссылки можно прочитать в «Locating Files Using Bookmarks» из яблочной документации.
Автор: