Сосредоточение на атомарных операциях

Файлы, как известно, сложны, но, похоже, немного хуже в Swift с устаревшими классами и методами. Как мы можем сделать это немного проще? Хороший небольшой учебник с примерами кода? Хорошо, если ты этого хочешь.

Обеспечение атомарности операций означает, что операции будут завершены без промежуточных состояний и потенциально поврежденных данных.

Сложность: легко | Нормальный | Испытывающий

Предпосылки:

  • Некоторое знакомство с отладчиком
  • Обработка ошибок с помощью try

Терминология

Атомарные файловые операции: методы, которые нельзя прервать или частично завершить.

Файловая система: обеспечивает постоянное хранение файлов данных, приложений и файлов, связанных с самой операционной системой.

SearchPathDirectory: расположение важных папок на устройствах iOS.

Песочница: набор детализированных элементов управления, ограничивающих доступ приложения к файлам, настройкам, сетевым ресурсам и оборудованию.

Песочница

iOS помогает вам взаимодействовать с файловой системой, не позволяя конкурирующим приложениям вносить изменения в ваши файловые каталоги. Это означает, что у вас как у приложения также есть ограничения. В любом случае, вот взгляд Apple на взаимодействие приложения с файловой системой:

For security purposes, an iOS app’s interactions with the file system are limited to the directories inside the app’s sandbox directory. During installation of a new app, the installer creates a number of container directories for the app inside the sandbox directory. Each container directory has a specific role. The bundle container directory holds the app’s bundle, whereas the data container directory holds data for both the app and the user. The data container directory is further divided into a number of subdirectories that the app can use to sort and organize its data. The app may also request access to additional container directories — for example, the iCloud container–at runtime.

Лучшие советы

Просмотр ваших файлов

Используя симулятор, вы можете увидеть файлы, которые в настоящее время находятся в вашем комплекте приложений. Где?

User>Library>Developer>CoreSimulator>Devices

Чтобы узнать, какой GUID используется в вашем приложении, вы можете использовать следующую команду LLDB:

po NSHomeDirectory()

чтобы вернуть ваш домашний каталог из отладчика, который возвращает что-то вроде следующего:

“/Users/user/Library/Developer/CoreSimulator/Devices/37A85B0B-F2B7–4A0C-BAA7-E05A831FFAE0/data/Containers/Data/Application/AC630F5A-71D2–4DE4–82E2-D7CF328015C8”

и файлы будут даже сохранены в этой папке или в папке библиотеки, содержащейся в этой папке.

Папки с файлами

Вы будете знать, что в файловой системе есть папки. Вы можете добавлять файлы в папки в вашей файловой системе, чтобы ваша работа оставалась красивой и организованной.

Ограничения в предоставленном коде

Обработка ошибок

Представленный код не обрабатывает ошибки. Это для краткости, но НЕ огорчайтесь, если вы видите что-то там, где я просто распечатываю для вас ошибку.

SearchPathDirectory

Это важные каталоги, которые используются. В документации они упоминаются по очереди, но здесь я просто упоминаю те, на которые я буду ссылаться в этом руководстве.

applicationSupportDirectory: место для хранения файлов, которые будут использоваться в вашем приложении.

cachesDirectory: хорошее место для хранения кешей, которые можно удалить в вашем приложении.

itemReplacementDirectory: временный каталог

Пути или URL-адреса

Часто бывает сочетание URL и файлов.

Путь имеет следующий формат, в данном случае - к файлу file.json.

/Users/user/Library/Developer/CoreSimulator/Devices/37A85B0B-F2B7–4A0C-BAA7-E05A831FFAE0/data/Containers/Data/Application/9FC4F273–2AD5–48E1-BCEC-5A2CCDD6CD7F/Library/Application Support/file.json

URL может иметь следующий формат

file:///Users/user/Library/Developer/CoreSimulator/Devices/37A85B0B-F2B7–4A0C-BAA7-E05A831FFAE0/data/Containers/Data/Application/9EE0E3F2-CC53–4462-B63C-949B8C98A3BD/Library/Application%20Support/StagingBundle.bundle/Version.json

обратите внимание, что URL-адрес имеет file: /// в дополнение к пути к файлу.

Абсолютные пути, такие как показанные выше, дают полный путь к файлу.

Как правило, использование URL-адресов предпочтительнее путей, поскольку они считаются более гибкими. Однако в FileManager есть методы, которые могут работать с обоими способами, поэтому может быть лучше выбрать один из URL-адресов и путей к файлам и придерживаться его (но выбрать URL-адрес, да?)

Простые действия (которые вы можете использовать)

Эти функции предназначены для упрощения копирования и вставки и подходят для использования в любых приложениях.

CreateDirectory

FileManager позволяет создавать каталоги либо с URL-адресом, либо с путем к файлу.

try fileManager.createDirectory(at: newURL, withIntermediateDirectories: true, attributes: nil)

или альтернативно с путем к файлу

try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)

В приведенных здесь примерах я выберу URL-адреса

Теперь, когда это более интересно, мы можем создать каталог с именем папки и в SearchPathDirectory по вашему выбору:

Это дает следующий результат:

Вы должны помнить, что каталог поддержки приложений не гарантированно присутствует в файловой системе в любое время. Здесь мы избежали ошибки, поскольку логическое значение withIntermediateDirectories имеет значение true.

RemoveDirectory

FileManager позволяет нам удалять элементы.

try fileManager.removeItem(at: originURL)

Точно так же мы можем использовать эквивалент пути к файлу

try fileManager.removeItem(atPath: originURL)

и еще раз здесь мы рассмотрим использование url

который удаляет каталог, добавленный выше.

Запись файлов

FileManager имеет метод createFile, который записывает некоторые данные.

Чтобы получить эти данные, я просто преобразовываю строку в данные для вас.

let dataToWrite = "My Interesting Data".data(using: .utf8)

Затем мы можем использовать createFile FileManager (atPath

func create(atPath path: String, contents data: Data?, attributes attract: [FileAttributeKey : Any]? = nil) -> Bool

Теперь, к сожалению, эта функция принимает только путь к файлу, а также ее вызов не генерирует, а дает логический результат. Кроме того, этот метод не является атомарным, поэтому довольно сложно узнать, завершилась ли наша операция записи в любой момент времени.

Многие люди забывают сначала создать файл, теперь не волнуйтесь - я вам помог:

Но настоящая проблема здесь в том, что API использует atPath. Гораздо лучше использовать API, поддерживающий URL-адреса, и даже лучше, если бы мы могли писать атомарно (то есть гарантировать, что запись завершится.

Итак, нам снова понадобятся данные для записи, а затем также вызовем метод

let dataToWrite = "My Interesting Data".data(using: .utf8)
createFileToURL(withData: dataToWrite, withName: "MyFile.txt")

Его можно легко обновить, чтобы мы могли создавать подпапки. Однако, если подпапка еще не создана, будет выдана ошибка. В этом случае нам тоже нужно создать подкаталог!

let dataToWrite = "My Interesting Data".data(using: .utf8)
createFileToURL(withData: dataToWrite, withName: "MyFile.txt", withSubDirectory: "test")

Что даст следующий результат:

Удалить элементы

FileManager имеет метод .removeItem, у которого есть выбор: использовать URL или путь к файлу.

try fileManger.removeItem(atPath: originPath)
try fileManger.removeItem(at: originURL)

Что можно (как и раньше) превратить в метод многократного использования:

и это тривиально обновить, чтобы иметь дело с подпапками в пути

Теперь я предпочитаю создать метод для записи строки непосредственно в каталог. Как оказалось, написать эту функцию несложно, и это было достигнуто следующим образом:

Копирование предметов

Теперь мы в курсе событий, и API мы можем использовать fileManager.copyItem для перемещения элемента, независимо от того, использует ли он для этого filePath или URL.

try fileManager.copyItem(at: originURL, to: destinationURL)
try fileManager.copyItem(atPath: originPath, toPath: destinationPath)

Но здесь есть одна проблема. Как оказалось, большая проблема.

Эта операция копирования файла не атомарна. Это означает, что мы не можем гарантировать его завершение.

Лучший способ - прочитать содержимое файла во временный файл (и, действительно, каталог, поскольку .itemReplacementDirectory здесь является хорошим выбором).

Создайте временный файл

Таким образом, мы можем создать временный файл, используя атомарную операцию data.write.

Замените временный файл, используя атомарную операцию data.write

Перемещение предметов

Теперь перемещение похоже на копирование, верно?

Также есть методы FileManager для перемещения

try fileManager.moveItem(at: originURL, to: destinationURL)
try fileManager.moveItem(atPath: originPath, toPath: destinationPath)

но, что довольно удручающе (и предсказуемо), они не атомарны.

Теперь перемещение аналогично копированию, но исходный файл удаляется после операции.

Теперь вы можете вспомнить это

fileManger.removeItem(at: originURL)

не гарантируется, что он будет атомарным.

Так что делать?

Что ж, хотя removeItem не атомарен, это короткая операция. Во-вторых, на практике мы удаляем только ссылку на файл (как эта конкретная операция может быть неполной), и она также возвращает логическое значение в зависимости от того, завершена операция или нет.

Если вас не устраивает этот ответ, есть другое решение; возьмите исходный файл и протрите его пустым файлом (это атомарная операция). Я полагаю, вам нужно будет где-то установить логическое значение, чтобы указать, что к файлу нельзя обращаться, но я думаю, для вас это не будет проблемой.

Меня не волнуют атомные

Вы говорите о ядерной энергии?

Меня не волнуют атомные операции

Атомарные операции означают, что ваши файлы не повреждены. Либо они закончены, либо нет. Это может быть чрезвычайно важно, когда использование ваших файлов означает сбой приложения, сбой в работе приложения или что-то еще хуже (что-то вроде поломки серверной части…). Если вам нужно использовать файлы в вашем приложении (а есть причины, по которым вам это может понадобиться), вам нужно хорошенько подумать об этих вещах.

Полная ссылка на GitHub

Вот заполненная ссылка:



Почему бы не войти в контакт?

Есть вопросы? Я отвечу на них в комментариях или в Твиттере.