解析:将PFObject子类与PFCloud调用一起使用

时间:2013-11-23 04:14:12

标签: ios objective-c parse-platform subclassing

我有一个名为MediaFile的PFObject子类。为了从调用服务器中获取子类的实例,我已经看到了如何从Parse子类构造查询,如下所示:

PFQuery *query = [MediaFile query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { ... }];

但是,我的应用程序中的大多数服务器调用都是自定义云调用,它返回PFObjects。一旦我检索了这些对象,我就想将它们视为MediaFile对象,并在它们上调用MediaFile类中定义的自定义方法。转换不起作用,因为它实际上并不构造MediaFile对象。我为克服这个问题所做的是为我使用[MediaFile object]取回的每个PFObject构建新的MediaFiles,然后使用我编写的名为loadFromObject:的方法将所有数据复制到每个PFObject中:

[PFCloud callFunctionInBackground:@"func" withParameters:@{} block:^(id objects, NSError *error) {

  for (PFObject *object in objects) {

    MediaFile *mf = [[MediaFile object] loadFromObject:object];

    [array addObject:mf];
  }
}];

在MediaFile.m中:

- (MediaFile *) loadFromObject:(PFObject *)object {
  NSArray *keys = [object allKeys];

  for (NSString *key in keys) {
    id obj = [object objectForKey:key];
    [self setObject:obj forKey:key];
  }

  return self;
}

这样可行,但是有更优雅的方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:1)

您的自定义类是如何定义的? 因为它们的子类化机制依赖于键值编码(KVC)。 这是KVC Programming Guide。或者您可以查看here上的指南。

以下示例应该有效。


在标题中:

@interface CustomObject : PFObject <PFSubclassing>

@property (nonatomic, strong) NSString *propertyName;

在实施中:

#import <Parse/PFObject+Subclass.h>

#pragma mark - columns of Parse table
@dynamic propertyName;

#pragma mark - regiester parse subclass in runtime
+ (void)load {
    @autoreleasepool {
        [self registerSubclass];
    }
}

#pragma mark - parse cloud table name
+ (NSString *)parseClassName
{
    return @"CustomObject"; // table name
}

答案 1 :(得分:1)

<强> TL; DR: 当您的Cloud代码从网络调用中返回这些对象时,请确保您的子类PFObject已在Parse注册。

如果您使用the instructions Parse includes in it's documentation guidePFObject进行了子类化,则可以在第一次返回自定义子类的网络调用之前进行以下调用:

CustomSubclass.initialize()

添加此代码的好地方是在您的app delegate的application(didFinishLaunchingWithOptions:)方法中,它可能会在与您的子类相关的任何其他代码之前运行。

带解释的长版本

我的情况与你的情况略有不同,但我认为问题基本相同。我的问题是我通过PFQuery获取对象,查询回调中返回的对象只是通用的PFObject

以下是我的代码:

// Scene is a PFObject subclass func getLatestScenes(completion: ((scenes: [Scene]) -> Void)?) { var query = PFQuery(className: SceneClassName) query.findObjectsInBackgroundWithBlock { (results: [AnyObject]?, error: NSError?) -> Void in if let scenes = results as? [Scene] { // This would never run, as the members of `results` were only PFObjects completion?(scenes: scenes) } else { // Code would always skip to the empty result case completion?(scenes: []) } } }

results数组的各个成员将在调试器中打印出来,其中的描述清楚地表明它们应该按照预期属于Scene类,但在检查时它们只是{{ 1}} S上。

PFObject

这里的问题是(lldb) po results![0] <Scene: 0x7fc743a8f310, objectId: YyxYH9dtBp, localId: (null)> { creator = "<PFUser: 0x7fc74144b320, objectId: FV3cmDI1PW>"; sceneDataFile = "<PFFile: 0x7fc743a96920>"; } (lldb) p results![0] (PFObject) $R6 = 0x00007fc743a8f310 { NSObject = { isa = 0x00007fc743a8f310 } ... // Info on object... ... }对象没有在Parse中正确注册为Scene子类。 PFObject方法被正确覆盖,如下所示:

initialize

但是在调用查询时没有调用override class func initialize() { struct Static { static var onceToken : dispatch_once_t = 0; } dispatch_once(&Static.onceToken) { self.registerSubclass() } }函数。问题是在第一个消息发送到该类之前调用​​了类的initalize方法,但在执行查询之前没有消息发送到initialize。将以下行添加到AppDelegate的Scene解决了问题

application(didFinishLaunchingWithOptions:)

之后,Parse能够在客户端上推断出对象应该是哪个类,并使用该类构造结果数组。

答案 2 :(得分:0)

毕竟它很简单。对我有用的是将返回的PFObject简单地分配给PFObject的Objective-C子类。所以在你的情况下,

[PFCloud callFunctionInBackground:@"func" withParameters:@{} block:^(id objects, NSError *error) {

  for (PFObject *object in objects) {

    MediaFile *mf = object; // simple as that

    [array addObject:mf];
  }
}];