Статьи

Как сохранить пользовательские данные с помощью SQLite или базовых данных на устройствах Apple

При создании приложения для iOS необходимо учитывать концепцию «постоянство данных». Постоянство данных — это метод удержания данных в случае сброса приложения или устройства. Никто не хочет играть в игру или обновлять свою чековую книжку и потерять все после сброса, потому что плохо спроектированное приложение не сохранило их информацию. Существует много способов обеспечить сбор и сохранение пользовательских данных, но два метода, которые я хочу выделить, — это использование баз данных SQLite и каркас основных данных Apple.

Apple имеет встроенную функциональность для использования баз данных SQLite в вашем приложении. Этот метод предназначен исключительно для баз данных SQLite, а не для баз данных mySQL. Какая разница? Основное различие между ними заключается в том, что базы данных SQLite встроены в приложение и представляют собой один автономный файл. Базы данных MySQL занимают гораздо больше места из-за их зависимости от нескольких файлов, и они не могут быть встроены в приложение. Вам понадобится специальный редактор для просмотра этих баз данных SQLite. Я использую и рекомендую SQLVue Lite . Существует также полная версия, которая стоит дороже и предлагает несколько дополнительных функций.

Среда Apple Core Data, в отличие от SQLite, использует более объектно-ориентированный подход к хранению пользовательских данных. С помощью Core Data вы создаете объекты, которые напоминают классы и объекты для хранения данных. Эти объекты хранятся в модели данных, которая используется для хранения различных свойств данных. Вы можете хранить атрибуты, которые похожи на переменные экземпляра в Objective-C, отношения между объектами и другими данными, а также извлеченные свойства, которые очень похожи на вышеупомянутые отношения. Единственное различие между отношениями и извлеченными свойствами состоит в том, что отношения вытягивают все данные преимущественно с объектом, в то время как извлеченные свойства распознают существование связанных данных, но не извлекают их сразу.

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

SQLite

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

(Примечание: все представленные примеры взяты из XCode 4.2)

Прежде чем начать, нам нужно включить динамическую библиотеку, которая обеспечит все необходимые функции для хранения данных. Найдите iOS SDK в своей системе и перейдите по адресу usr / lib / libsqlite3.dylib.

Чтобы получить доступ к базе данных, мы сначала должны открыть соединение с ней в коде. Эта часть удивительно проста. Код просто выглядит так:

sqlite3 *database;

int result = sqlite3_open(“/path/to/database/file”, &database);

Затем, когда вы закончите с этим, вы закроете утверждение.

 sqlite3_close(database);

Нет причин открывать и закрывать базу данных без внесения каких-либо значимых изменений. Весь смысл в том, чтобы использовать базу данных, поэтому давайте взглянем на команду sqlite3_exec. В этом есть небольшая хитрость. Можно подумать, что вы будете использовать эту команду во всех случаях, но это не так. Это возможно только для операторов SQL, которые не возвращают никаких данных. Это будет включать команды DQL, такие как CREATE, INSERT, UPDATE, DELETE и т. Д.

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

 NSString *query = @”SELECT ID, FIELD_DATA FROM FIELDS ORDER BY ROW”;

sqlite3_stmt = *statement;

int result = (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil);

while(sqlite3_step(statement) == SQLITE_ROW){

int rowNum = sqlite3_column_int(statement, 0);

char *rowData = (char *)sqlite3_column_text(statement, 1);

NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];

// do something with the data

[fieldValue release];

}

sqlite3_finalize(statement);

В приведенной выше разметке есть несколько важных шагов. Сначала мы создаем наш оператор SQL. Затем мы должны подготовить базу данных для использования. Рассматривая функцию sqlite3_prepare_v2, мы видим, что первый параметр — это переменная базы данных, которую мы создали. Затем мы берем нашу строку запроса и превращаем ее в C-строку на основе UTF8. Наконец, у нас есть обратное утверждение. У нас есть возможность хранить любые неиспользованные части запроса, но мы устанавливаем его равным nil.

Как только у нас есть эти операторы, мы начинаем циклически перебирать все результаты, используя sqlite3_step (оператор) и проверяя, что каждый из них становится строкой в ​​базе данных. Остальное довольно просто. Сначала мы выясняем, в какой строке мы находимся и какие данные она хранит, и сохраняем ее в объекте NSString, который мы затем можем использовать. Это та часть, где вы можете проявить творческий подход к данным. Очень распространенной тактикой является хранение данных в виде NSDictionary или NSArray, которые будут использоваться при отображении большого количества информации или при использовании табличного представления.

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

 char *sql = “insert into foo values (?,?);”;

sqlite3_stmt *stmt;

if(sqlite3_prepare_v2(database, sql, -1, &stmt, nil) == SQLITE_OK){

sqlite3_bind_int(stmt, 1, 235);

sqlite3_bind_text(stmt, 2, ”bar”, -1, NULL);

}

if(sqlite3_step(stmt) != SQLITE_DONE){

NSLog(@”This should be real error checking!”);

}

sqlite3_finalize(stmt);

Методы обработки между двумя методами различаются по нескольким причинам. Еще раз, мы начинаем с создания запроса и дескриптора оператора. Обратите внимание, что в строке запроса есть два знака вопроса. Они станут ценностями после того, как мы их свяжем. Чтобы связать их, мы используем команды sqlite3_bind, добавляя одно целое и одно текстовое значение. Первый параметр — это дескриптор оператора, а затем, куда мы хотим его поместить (первый знак вопроса — 1, второй — 2), за которым следует значение.

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

Чтобы начать с Core Data, вам нужно создать модель данных, которую можно создать, нажав «Файл» -> «Новый» -> «Новый файл». Затем под iOS слева выберите «Основные данные», а затем «Данные». Модель «.

В этом новом файле вы создаете свои объекты, используя кнопку в левом нижнем углу представления. Идите вперед и создайте его. Я просто оставил свой как «Entity». Теперь, когда у нас есть начало Core Data, мы можем начать копаться в коде.

Начните с импорта платформы CoreData из любого места, где у вас есть iOS SDK, и добавьте <CoreData / CoreData.h> AppDelegate.h в свое приложение. Кроме того, импортируйте AppDelegate.h в ваш ViewController.m. Затем в ViewController.m внесите следующее изменение в функцию ViewDidLoad:

 - (void)viewDidLoad

{

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@&quot;Entity&quot; inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

[request setEntity:entityDescription];

NSError *error;

NSArray *objects = [context executeFetchRequest:request error:&amp;error];

if(objects == nil){

NSLog(@&quot;There was an error!&quot;);

}

for(NSManagedObject *oneObject in objects){

NSNumber *lineNum = [oneObject valueForKey:@&quot;lineNum&quot;];

NSString *lineText = [oneObject valueForKey:@&quot;lineText&quot;];

NSString *fieldName = [NSString stringWithFormat:@&quot;line%d&quot;, [lineNum integerValue]];

UITextField *theField = [self valueForKey:fieldName];

theField.text = lineText;

}

[request release];

}

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

 NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@&quot;Entity&quot; inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

[request setEntity:entityDescription];

Основные данные работают в контексте. Контекст используется для ссылки, где хранятся все объекты. Затем мы берем описание нашей сущности (называемой «сущность») в том контексте, который мы определили. Наконец, мы просто запрашиваем все атрибуты, отношения и извлекаемые свойства этой конкретной сущности.

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

Перед запуском этого кода убедитесь, что вы добавили следующее в AppDelegate.h.

 NSManagedObjectContext *managedContext_;

NSManageObjectModel *managedObjectModel_;

NSPersistentStoreCoordinator *persistentStoreCoordinator_;

Кроме того, обязательно добавьте свойства для них и синтезируйте их в файле реализации.

Итак, в чем ключевое различие между ними? Метод SQLite был довольно сложным с синтаксисом, и для него требовалось, чтобы все строковые значения были основаны на C в формате UTF8. Преимущество SQLite в том, что файлы можно легко просматривать, чтобы увидеть взаимосвязи между различными данными. И наоборот, метод Core Data основан на Objective-C и хранит данные как управляемые объекты. Это позволяет создать дружественный интерфейс для создания объектов и добавления свойств. Здесь задействовано еще немного кода, и использование Core Data может привести к большему размеру файла по сравнению с меньшими базами данных SQLite, но это позволяет очень легко получить и определить, какой код должен быть запущен для извлечения данных.

Один лучше другого? Не обязательно. Я лично предпочитаю использовать Core Data, так как он делает всю грязную работу за вас. Но если вам нужно легкое, оптимизированное приложение или если вы работаете с очень небольшим объемом памяти, SQLite может лучше соответствовать вашим целям.