Ошибка фильтрации массива пользовательских объектов по NSNumber с использованием NSPredicate - PullRequest
0 голосов
/ 09 апреля 2020

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

Я загружаю данные из JSON в массив пользовательских объектов, которые выглядят так:

{"hid":"47","public":"1"}

Код для анализа JSON выглядит следующим образом:

 if (feedElement[@"public"] && ![feedElement[@"public"] isEqual:[NSNull null]]) {
            newMyObject.pub = feedElement[@"public"]== nil ? @"0" : feedElement[@"public"];}

Объект выглядит следующим образом:

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyObject : NSObject
@property (nonatomic, retain) NSNumber * hid; 
@property (nonatomic, retain) NSNumber * pub;
@end
NS_ASSUME_NONNULL_END

Объекты помещаются в NSArray * myObjects

Мой код NSP предиката и фильтра выглядит следующим образом:

NSPredicate *pubPred = [NSPredicate predicateWithFormat:@"pub == 1"];
NSArray *filteredArray = [myObjects filteredArrayUsingPredicate:pubPred];

Когда я регистрирую [myObjects valueForKey:@"pub"], он регистрируется как 1,1,1, et c. так что я знаю, что все значения для pub равны 1, однако результирующее filteredArray пусто.

Что может быть не так с моим кодом?

Спасибо за любые предложения.

Редактировать : Я изменил publi c на pub в объекте на случай, если publi c было зарезервированным словом, но оно ничего не изменило

1 Ответ

0 голосов
/ 14 апреля 2020

С примером кода: {"hid":"47","public":"1"}

newMyObject.pub = feedElement[@"public"]== nil ? @"0" : feedElement[@"public"];

Если значение public присутствует в JSON, вы установите свойство pub с feedElement[@"public"], что означает, что оно будет @ "1" (с образцом), что означает, что вы положите NSString.
. В случае public значение присутствует в JSON, вы установите свойство pub с помощью @ «0» означает, что вы поставите NSString.

. Неважно, объявлено ли оно @property (nonatomic, retain) NSNumber * pub;, вы устанавливаете NSString, а не NSNumber.

Хотите тестирование кода?

@interface MyObject : NSObject

@property (nonatomic, retain) NSNumber *hid;
@property (nonatomic, retain) NSNumber *pub;

+(void)test;
@end

И

@implementation MyObject

-(id)initWithPub:(NSNumber *)pub andHID:(NSNumber *)hid {
    self = [super init];
    if (self) {
        self.pub = pub;
        self.hid = hid;
    }
    return self;
}

-(NSString *)description {
    return [NSString stringWithFormat:@"%@ pub: %@ - hid: %@", [super description], [self pub], [self hid]];
}

+(NSArray *)arrayList {
    return @[[[MyObject alloc] initWithPub:@1 andHID:@3], //Here with real NSNUmbner
             [[MyObject alloc] initWithPub:@"1" andHID:@"2"]]; //Here with NSString instead
}

+(void)test {
    NSArray *list = [MyObject arrayList];

    NSPredicate *predicateNSNumber = [NSPredicate predicateWithFormat:@"pub == %@", @1];
    NSArray *filteredNSNumber = [list filteredArrayUsingPredicate:predicateNSNumber];
    NSLog(@"Filtered-NSNumber: %@", filteredNSNumber);

    NSPredicate *predicateNSString = [NSPredicate predicateWithFormat:@"pub == %@", @"1"];
    NSArray *filteredNSString = [list filteredArrayUsingPredicate:predicateNSString];
    NSLog(@"Filtered-NSString: %@", filteredNSString);
}

Вывод:

$> Filtered-NSNumber: (
    "<MyObject: 0x60000024cba0> pub: 1 - hid: 3"
)
$> Filtered-NSString: (
    "<MyObject: 0x60000024d1e0> pub: 1 - hid: 2"
)

Вы можете заявить: «Да, но у вас есть предупреждение в [[MyObject alloc] initWithPub:@"1" andHID:@"2"]: Incompatible pointer types sending 'NSString *' to parameter of type 'NSNumber *', да, у меня есть. У вас тоже должно быть это.

На самом деле, оно у вас тоже будет, если вы написали свою троицу, если:

if (feedElement[@"public"] == nil) {
    newMyObject.pub = @"0"; //Incompatible pointer types assigning to 'NSNumber * _Nonnull' from 'NSString *'
} else {
    newMyObject.pub = feedElement[@"public"]; //Here, no warning, because it's hope you know what you are doing and setting a NSNumber here.
}

Как насчет использования этого в вашем коде для проверки:

for (MyObject *anObject in list) {
    NSLog(@"Testing: %@", anObject);
    if ([[anObject pub] isKindOfClass:[NSString class]]) {
        NSLog(@"Pub is a String");
    } else if ([[anObject pub] isKindOfClass:[NSNumber class]]) {
        NSLog(@"Pub is a NSNumber");
    }
    NSLog(@"Attempt to call -stringValue which is a NSNumber method, not a NSString one");
    NSLog(@"Attempt Call: %@\n", [[anObject pub] stringValue]);
}

Вы должны получить ошибку -[__NSCFConstantString stringValue]: unrecognized selector sent to instance, потому что это действительно NSString, а не NSNumber.

Решения:

Вам нужно исправить парсинг или изменить тип свойства в MyObject.

С keepin г собственности как NSNumber:

if ([feedElement[@"public"] isKindOfClass:[NSString class]]) {
     self.pub = @([feedElement[@"public"] integerValue]); //Transform it into a NSInteger, then into a NSNumber with @(someInteger)
} else ([feedElement[@"public"] isKindOfClass:[NSNumber class]]) {
    self.pub = feedElement[@"public"]; //It's already a NSNumber instance
} else {
    self.pub = @(0); //Default value because unknown class or not present 
}
...