我的问题: saveInBackground没有工作。
不起作用的原因是:,我将存储在NSArray中的PFObjects保存为使用NSKeyedArchiving进行文件处理。我这样做的方法是通过这个NSCoding实现图书馆。由于一些我不知道的原因,正在添加其他几个字段,并将其设置为NULL。我有一种感觉,这是搞砸了对saveInBackground的API调用。当我对第一组对象(在saveInBackground之前)调用NSKeyedArchiving时,saveInBackground工作得很好。但是,当我对第二个对象(在NSKeyedArchiving之后)调用它时,它不会保存。为什么会这样呢?
Save
[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的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发布于 2014-05-22 02:06:39
我创建了一个非常简单的解决方案,不需要更改上面的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创建一个临时对象,并保存它。这是一个工作解决方案,不会在服务器上创建对象的副本。感谢所有帮助过的人。
发布于 2014-05-19 01:23:03
在"<null>"之后获取所有NSArchiving条目的原因是您使用的NSCoding库处理零解析属性的方式。特别是,在2月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在4月22日的答复中提出了另一个图书馆。
更新:
由于替代库缺少对PFFile的支持,下面是实现NSCoding for 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;
@endPFFile+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 + 马丁·雷巴克的PFFile编码器)确实有效--在下面的测试工具中(请参阅下面的代码),对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服务器重新保存原始对象。同时,请投票和/或接受答案,因为它解决了在NSKeyedArchiving之后使用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];
}
} ];
}发布于 2014-04-22 13:24:51
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分类向PFObject添加NSCoder支持。它不同于您链接到的库中的实现,但我不确定它有多可靠。这可能是值得尝试的。
https://stackoverflow.com/questions/23119739
复制相似问题