Анализ: использование подклассов PFObject с вызовами PFCloud

У меня есть подкласс PFObject под названием MediaFile. Чтобы вернуть экземпляры моего подкласса из вызовов на сервер, я видел, как вы можете создавать запросы из подклассов Parse следующим образом:

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

Однако большинство вызовов сервера в моем приложении — это пользовательские вызовы Cloud, которые возвращают PFObjects. Как только я извлек эти объекты, я хочу рассматривать их как объекты MediaFile и вызывать для них пользовательские методы, определенные в классе MediaFile. Кастинг не работает, потому что он фактически не создает объект MediaFile. Что я сделал, чтобы решить эту проблему, так это создал новые MediaFiles для каждого PFObject, который я возвращаю, используя [MediaFile object], а затем скопировал все данные в каждый, используя метод, который я написал под названием loadFromObject::

[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;
}

Это работает, но есть ли более элегантный способ решения этой проблемы?


person Andrew    schedule 23.11.2013    source источник


Ответы (3)


Как определяется ваш пользовательский класс? Потому что их механизм подкласса основан на кодировании ключ-значение (KVC). Вот Руководство по программированию KVC. Или вы можете ознакомиться с руководством по адресу здесь.

Следующий пример должен работать.


В заголовке:

@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
}
person KK'Pan    schedule 13.02.2014

TL;DR: убедитесь, что ваш подкласс PFObject зарегистрирован в Parse к тому времени, когда ваш облачный код вернет эти объекты из сетевого вызова.

Если вы создали подкласс PFObject, используя инструкции, которые Parse включает в руководство по документации , вы можете просто сделать следующий вызов перед первым сетевым вызовом, который возвращает ваш собственный подкласс:

CustomSubclass.initialize()

Этот код лучше всего добавить в метод 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, как и предполагалось, но при проверке их было только 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... ... }

Проблема заключалась в том, что объект Scene не был должным образом зарегистрирован в Parse как подкласс PFObject. Метод initialize был правильно переопределен следующим образом:

override class func initialize() { struct Static { static var onceToken : dispatch_once_t = 0; } dispatch_once(&Static.onceToken) { self.registerSubclass() } }

но функция initalize не вызывалась к моменту вызова запроса. Проблема в том, что метод класса initialize вызывается непосредственно перед отправкой первого сообщения этому классу, но до выполнения запроса Scene не было отправлено ни одного сообщения. Добавление следующей строки в мой AppDelegate application(didFinishLaunchingWithOptions:) решило проблему

Scene.initialize()

После этого Parse смог сделать вывод, к какому классу на клиенте должны относиться входящие данные, которыми должны быть объекты, и сконструировал массив результатов с этим классом.

person Ziewvater    schedule 06.08.2015

Ведь это просто. Что сработало для меня, так это простое присвоение возвращенного PFObject подклассу Objective-C PFObject. Итак, в вашем случае

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

  for (PFObject *object in objects) {

    MediaFile *mf = object; // simple as that

    [array addObject:mf];
  }
}];
person Kostis    schedule 17.04.2014