Сохранение PFObject NSCoding

Моя проблема: saveInBackground не работает.

Причина. Это не работает. Я сохраняю PFObjects хранящиеся в NSArray, в файл с использованием NSKeyedArchiving . То, как я это делаю, – это внедрение NSCoding через эту библиотеку . По какой-то причине мне неизвестно, добавлено несколько других полей и установлено значение NULL. У меня такое чувство, что это вызывает API-вызов saveInBackground . Когда я вызываю saveInBackground на первый набор объектов (до NSKeyedArchiving ), saveInBackground работает просто отлично. Однако, когда я вызываю его на втором объекте (после NSKeyedArchiving ), он не сохраняется. Почему это?

Сохранить

 [NSKeyedArchiver archiveRootObject:_myArray toFile:[self returnFilePathForType:@"myArray"]]; 

поиск

 _myArray = (NSMutableArray *)[NSKeyedUnarchiver unarchiveObjectWithFile: [self returnFilePathForType:@"myArray"]]; 

Объект перед NSArchiving

 2014-04-16 16:34:56.267 myApp[339:60b] <UserToMessage:bXHfPM8sDs:(null)> { from = "<PFUser:sdjfa;lfj>"; messageText = "<MessageText:asdffafs>"; read = 0; to = "<PFUser:asdfadfd>"; } 2014-04-16 16:34:56.841 myApp[339:60b] <UserToMessage:bXHsdafdfs:(null)> { from = "<PFUser:eIasdffoF3gi>"; messageText = "<MessageText:asdffafs>"; read = 1; to = "<PFUser:63sdafdf5>"; } 

Объект после NSArchiving

 <UserToMessage:92GGasdffVQLa:(null)> { ACL = "<null>"; createdAt = "<null>"; from = "<PFUser:eIQsadffF3gi>"; localId = "<null>"; messageText = "<MessageText:EudsaffdHpc>"; objectId = "<null>"; parseClassName = "<null>"; read = 0; saveDelegate = "<null>"; to = "<PFUser:63spasdfsxNp5>"; updatedAt = "<null>"; } 2014-04-16 16:37:46.527 myApp[352:60b] <UserToMessage:92GadfQLa:(null)> { ACL = "<null>"; createdAt = "<null>"; from = "<PFUser:eIQsadffF3gi>"; localId = "<null>"; messageText = "<MessageText:EuTndasHpc>"; objectId = "<null>"; parseClassName = "<null>"; read = 1; saveDelegate = "<null>"; to = "<PFUser:63spPsadffp5>"; updatedAt = "<null>"; } 

Обновление с использованием Florent's PFObject Категория:

 PFObject+MyPFObject_NSCoding.h #import <Parse/Parse.h> @interface PFObject (MyPFObject_NSCoding) -(void) encodeWithCoder:(NSCoder *) encoder; -(id) initWithCoder:(NSCoder *) aDecoder; @end @interface PFACL (extensions) -(void) encodeWithCoder:(NSCoder *) encoder; -(id) initWithCoder:(NSCoder *) aDecoder; @end PFObject+MyPFObject_NSCoding.m #import "PFObject+MyPFObject_NSCoding.h" @implementation PFObject (MyPFObject_NSCoding) #pragma mark - NSCoding compliance #define kPFObjectAllKeys @"___PFObjectAllKeys" #define kPFObjectClassName @"___PFObjectClassName" #define kPFObjectObjectId @"___PFObjectId" #define kPFACLPermissions @"permissionsById" -(void) encodeWithCoder:(NSCoder *) encoder{ // Encode first className, objectId and All Keys [encoder encodeObject:[self className] forKey:kPFObjectClassName]; [encoder encodeObject:[self objectId] forKey:kPFObjectObjectId]; [encoder encodeObject:[self allKeys] forKey:kPFObjectAllKeys]; for (NSString * key in [self allKeys]) { [encoder encodeObject:self[key] forKey:key]; } } -(id) initWithCoder:(NSCoder *) aDecoder{ // Decode the className and objectId NSString * aClassName = [aDecoder decodeObjectForKey:kPFObjectClassName]; NSString * anObjectId = [aDecoder decodeObjectForKey:kPFObjectObjectId]; // Init the object self = [PFObject objectWithoutDataWithClassName:aClassName objectId:anObjectId]; if (self) { NSArray * allKeys = [aDecoder decodeObjectForKey:kPFObjectAllKeys]; for (NSString * key in allKeys) { id obj = [aDecoder decodeObjectForKey:key]; if (obj) { self[key] = obj; } } } return self; } @end 

Причина, по которой вы получаете все записи "<null>" после NSArchiving, объясняется тем, как используемая вами библиотека NSCoding обрабатывает свойства nil Parse. В частности, в фиксации 18 февраля произошло несколько изменений в обработке nil, включая удаление нескольких тестов, чтобы узнать, было ли свойство нулевым плюс добавление следующего кода внутри декодирования:

  //Deserialize each nil Parse property with NSNull //This is to prevent an NSInternalConsistencyException when trying to access them in the future for (NSString* key in [self dynamicProperties]) { if (![allKeys containsObject:key]) { self[key] = [NSNull null]; } } 

Я предлагаю вам использовать альтернативную библиотеку NSCoding.

@AaronBrager предложил альтернативную библиотеку в своем ответе 22 апреля.

ОБНОВЛЕНО:

Поскольку в альтернативной библиотеке отсутствует поддержка PFFile, ниже приведена категория реализации изменений, необходимых для реализации NSCoding для PFFile. Просто скомпилируйте и добавьте PFFile+NSCoding.m в свой проект. Эта реализация выполняется из исходной библиотеки NSCoding, которую вы использовали.

PFFile+NSCoding.h

 // // PFFile+NSCoding.h // UpdateZen // // Created by Martin Rybak on 2/3/14. // Copyright (c) 2014 UpdateZen. All rights reserved. // #import <Parse/Parse.h> @interface PFFile (NSCoding) - (void)encodeWithCoder:(NSCoder*)encoder; - (id)initWithCoder:(NSCoder*)aDecoder; @end 

PFFile+NSCoding.m

 // // PFFile+NSCoding.m // UpdateZen // // Created by Martin Rybak on 2/3/14. // Copyright (c) 2014 UpdateZen. All rights reserved. // #import "PFFile+NSCoding.h" #import <objc/runtime.h> #define kPFFileName @"_name" #define kPFFileIvars @"ivars" #define kPFFileData @"data" @implementation PFFile (NSCoding) - (void)encodeWithCoder:(NSCoder*)encoder { [encoder encodeObject:self.name forKey:kPFFileName]; [encoder encodeObject:[self ivars] forKey:kPFFileIvars]; if (self.isDataAvailable) { [encoder encodeObject:[self getData] forKey:kPFFileData]; } } - (id)initWithCoder:(NSCoder*)aDecoder { NSString* name = [aDecoder decodeObjectForKey:kPFFileName]; NSDictionary* ivars = [aDecoder decodeObjectForKey:kPFFileIvars]; NSData* data = [aDecoder decodeObjectForKey:kPFFileData]; self = [PFFile fileWithName:name data:data]; if (self) { for (NSString* key in [ivars allKeys]) { [self setValue:ivars[key] forKey:key]; } } return self; } - (NSDictionary *)ivars { NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; unsigned int outCount; Ivar* ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i++){ Ivar ivar = ivars[i]; NSString* ivarNameString = [NSString stringWithUTF8String:ivar_getName(ivar)]; NSValue* value = [self valueForKey:ivarNameString]; if (value) { [dict setValue:value forKey:ivarNameString]; } } free(ivars); return dict; } @end 

ВТОРОЕ ОБНОВЛЕНИЕ:

Обновленное решение, которое я описал (используя комбинацию кодов Florent's PFObject / PFACL, заменяющих className с помощью parseClassName плюс кодер parseClassName Мартина Рыбака) РАБОТАЕТ – в тестовом жгуте ниже (см. Код ниже) второй вызов saveInBackground вызывает работу после восстановления от NSKeyedUnarchiver .

 - (void)viewDidLoad { [super viewDidLoad]; PFObject *testObject = [PFObject objectWithClassName:@"TestObject"]; testObject[@"foo1"] = @"bar1"; [testObject saveInBackground]; BOOL success = [NSKeyedArchiver archiveRootObject:testObject toFile:[self returnFilePathForType:@"testObject"]]; NSLog(@"Test object after archive (%@): %@", (success ? @"succeeded" : @"failed"), testObject); testObject = [NSKeyedUnarchiver unarchiveObjectWithFile:[self returnFilePathForType:@"testObject"]]; NSLog(@"Test object after restore: %@", testObject); // Change the object testObject[@"foo1"] = @"bar2"; [testObject saveInBackground]; } - (NSString *)returnFilePathForType:(NSString *)param { NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *filePath = [docDir stringByAppendingPathComponent:[param stringByAppendingString:@".dat"]]; return filePath; } 

Однако, глядя на сервер Parse, второй вызов saveInBackground создал новую версию объекта.

Несмотря на то, что это выходит за рамки первоначального вопроса, я посмотрю, можно ли заставить Parse-сервер повторно сохранить исходный объект. Тем временем, пожалуйста, голосуйте и / или принимайте ответ, учитывая, что он решает вопрос об использовании saveInBackground после NSKeyedArchiving .

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ:

Эта проблема оказалась просто временной проблемой – первая saveInBackground не завершилась, когда NSKeyedArchiver произошел – следовательно, objectId все еще был ник во время архивирования и, следовательно, был еще новым объектом во время второго saveInBackground. Используя блок (аналогичный ниже), чтобы обнаружить, когда сохранение завершено, и нормально звонить, NSKeyedArchiver также будет работать

Следующая версия не вызывает сохранения второй копии:

 - (void)viewDidLoad { [super viewDidLoad]; __block PFObject *testObject = [PFObject objectWithClassName:@"TestObject"]; testObject[@"foo1"] = @"bar1"; [testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (succeeded) { BOOL success = [NSKeyedArchiver archiveRootObject:testObject toFile:[self returnFilePathForType:@"testObject"]]; NSLog(@"Test object after archive (%@): %@", (success ? @"succeeded" : @"failed"), testObject); testObject = [NSKeyedUnarchiver unarchiveObjectWithFile:[self returnFilePathForType:@"testObject"]]; NSLog(@"Test object after restore: %@", testObject); // Change the object testObject[@"foo1"] = @"bar2"; [testObject saveInBackground]; } } ]; } 

Я создал очень простой обходной путь, который не требует изменений выше NSCoding библиотек NSCoding :

 PFObject *tempRelationship = [PFObject objectWithoutDataWithClassName:@"relationship" objectId:messageRelationship.objectId]; [tempRelationship setObject:[NSNumber numberWithBool:YES] forKey:@"read"]; [tempRelationship saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (succeeded) NSLog(@"Success"); else NSLog(@"Error"); }]; 

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

PFObject не реализует NSCoding , и похоже, что библиотека, которую вы используете, не правильно кодирует объект, поэтому ваш текущий подход не будет работать.

Подход, рекомендуемый Parse, заключается в том, чтобы кэшировать объекты PFQuery на диск, установив свойство cachePolicy :

 PFQuery *query = [PFQuery queryWithClassName:@"GameScore"]; query.cachePolicy = kPFCachePolicyNetworkElseCache; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { if (!error) { // Results were successfully found, looking first on the // network and then on disk. } else { // The network was inaccessible and we have no cached data for // this query. } }]; 

(Код из документации по кэшированию запросов ).

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

Свойство maxCacheAge объекта запроса задает, как долго что-то останется на диске до истечения срока его действия.


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

Как вы сказали в своем вопросе, нулевые поля должны saveInBackground вызовы saveInBackground .

Странно то, что parseClassName также является нулевым, в то время как это, вероятно, потребуется Parse для его сохранения. Установлен ли он перед сохранением NSArray в файле?

Поэтому я вижу два решения:

  • реализуя NSCoding без нулевых полей, но если объект уже сохранен на сервере, полезно (даже необходимо) сохранить его объекты, созданные, обновленные, а также поля …
  • сохраните каждый PFObject в Parse перед сохранением NSArray в файле, чтобы эти поля не были пустыми.
  • Phonegap и Parse.com Push Notifications IOS
  • Ошибка облачного кода уведомления о сбое?
  • Parse Unpin не удаляет объект из локального хранилища данных
  • Как запросить сообщение пользователя?
  • PGQueryTableViewController pagination не работает с heightForRowAtIndexPath
  • Xcode Parse.com сохранить в csv / любой файл?
  • Загрузка изображений из Parse iOS
  • xcode 7 не отображает данные в представлении таблицы синтаксиса в симуляторе, но работает на устройстве
  • Слушайте изменения событий iCal с сервера
  • Выделите столбец с помощью createdAt и updatedAt
  • Автоматизирует ли Parse данные из Facebook?
  • Interesting Posts

    Не найдено подходящих записей. Проверьте правильность идентификатора вашего пакета.

    iOS5: Исключение в UIWebView в модальном UIViewController, воспроизводящем видео Youtube

    Ограничения на push-уведомления для нескольких приложений с одного сервера

    Невозможно создать PDF и PRINT более 60 страниц (всплывает память и сбои)

    Создайте плейлист (начните следующую песню) быстрым

    UIButton .setImage () не работает в Swift 3

    Как выбрать строки текста в PDF, а затем выделить их? (IOS)

    Объект objc_setAssociatedObject недоступен в iOS?

    iOS – удаление всех записей, содержащих ключ на всех NSDiction, хранящихся в главном NSDictionary

    Google Analytics не отслеживает в Swift

    Как подсчитать выбранные строки UITableView в Swift. Необязательно в indexPathForSelectedRow

    Значения, которые я добавляю в брелок, можно сохранить после обновления приложения?

    Потенциальная утечка объекта, выделенного в iphone

    iOS: NSString теряет URL, (null) отображается

    Результат поиска UISearchDisplayController имеет другую структуру и поведение ячейки, чем таблица поиска

    Давайте будем гением компьютера.