Массив NSNumber containsObject не работает, поскольку типы разные, но они должны быть одинаковыми

У меня возникли проблемы с проверкой, содержит ли массив объект. Массив @property(nonatomic) NSArray<NSNumber*>* apiArray; возвращается как параметр из модели API, а объект, наличие которого я хочу проверить в массиве, возвращается из другой модели API @property(nonatomic) NSNumber* localObject;.

Когда я проверяю это [apiArray containsObject: localObject], он выводит 0, хотя значение есть.. Итак, я проверил типы, и на localObject тип фактически является NSNumber, в то время как, если я не ошибаюсь, результаты массива представляют собой массив NSString, как показано на рисунках ниже.

            NSLog(@"$$$$$$$$$$$ [apiArray containsObject: localObject]: %d", [apiArray containsObject: localObject] );
            NSLog(@"apiArray types are: %@", [apiArray valueForKey:@"class"]);
            NSLog(@"localObject type is: %@", [localObject valueForKey:@"class"]);
            NSLog(@"apiArray is: %@", apiArray);
            NSLog(@"localObject is: %@", localObject);



$$$$$$$$$$$ [apiArray containsObject: localObject]: 0
apiArray types are: (
    "__NSCFString"
)
localObject type is: __NSCFNumber
apiArray is: (
    5717271485874176
)
localObject is: 5717271485874176
API NSNumber array types are: (
    "__NSCFString"
)

Модели генерируются с помощью swagger-codegen.jar, но, как показывают параметры, они оба кажутся правильными.

В качестве теста я сам создал массив, и действительно, типы NSNumber и containsObject действительно работают так, как ожидалось.

NSMutableArray<NSNumber*>* apiArray = [NSMutableArray<NSNumber*> array];
            NSNumber *localObject = [NSNumber numberWithInt: -1];
            [apiArray addObject: localObject];

            NSLog(@"$$$$$$$$$$$ [apiArray containsObject: localObject]: %d", [apiArray containsObject: localObject] );
            NSLog(@"apiArray types are: %@", [apiArray valueForKey:@"class"]);
            NSLog(@"localObject type is: %@", [localObject valueForKey:@"class"]);
            NSLog(@"apiArray is: %@", apiArray);
            NSLog(@"localObject is: %@", localObject);

$$$$$$$$$$$ [apiArray containsObject: localObject]: 1
apiArray types are: (
    "__NSCFNumber"
)
localObject type is: __NSCFNumber
apiArray is: (
    "-1"
)
localObject is: -1
API NSNumber array types are: (
    "__NSCFNumber"
)

Можете ли вы определить, в чем может быть проблема?

Вы получаете с сервера числа в двойных кавычках?

Cy-4AH 19.06.2024 12:57

@ Cy-4AH Cy-4AH Я обновил вопрос, добавив отпечатки значений. Видимо, я получаю массив NSNumber с сервера, но до сих пор не понимаю, почему containsObject возвращает 0, когда объект действительно присутствует, а тип массива печатает NSString.

Vincenzo 19.06.2024 13:40

Ваш сервер отправляет строки вместо чисел.

Cy-4AH 19.06.2024 14:07

да, это выглядело бы так

Vincenzo 19.06.2024 14:09

Один из них — строка, другой — число, и чтобы иметь возможность «полностью сравнить» NSNumber, я бы вместо этого использовал integerValue.

Larme 19.06.2024 14:18

@Larme Я не уверен, что слежу за тобой. В моем первом фрагменте кода я использую значения модели API, печать массива не показывает значение между ", его тип значений — NSString, а containsObject возвращает 0. Во втором фрагменте я использую массив, который заполняю сам, и значение печатается между ", его тип значения действительно NSNumber, а containsObject правильно возвращает 1. где мне использовать integerValue? по объекту я хочу знать, присутствует ли он в массиве из API или где?

Vincenzo 19.06.2024 14:29

@Larme, ок... Я разобрался благодаря твоему предложению. Я начал с установки BOOL containsObject на false. Затем я перебираю значения массива и только когда containsObject имеет значение false (чтобы не переопределять возможное совпадение), я сравниваю их .integerValue с локальным объектом .integerValue и присваиваю полученное значение BOOL самому containsObject. Это работает и соответствует моей цели, но я все еще не могу понять, почему типы значений массива, возвращаемые с сервера, являются NSString, тогда как и модель, и json, которые мы получаем для ее компиляции, не являются таковыми.

Vincenzo 19.06.2024 15:17

Вы можете создать расширение для NSArray: -(BOOL)containsWhere:(BOOL (^)(id obj))predicate { NSInteger index = [self indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { return predicate(obj); }]; return index != NSNotFound; }, а затем назвать его так: BOOL contains = [array containsWhere:^BOOL(id _Nonnull obj) { return [obj integerValue] == [localObject integerValue]; }];

Larme 19.06.2024 15:18

Другое значение модели API (то, которое я хочу узнать, присутствует ли в массиве) компилируется как тест SNNumber.

Vincenzo 19.06.2024 15:18

@Vincenzo Как на самом деле извлекается apiArray? В Objective-C все элементы массива будут NSArray<NSNumber*>* не потому, что NSNumber, это скорее предложение/напоминание, на самом деле проверки НЕТ.

Larme 19.06.2024 15:20

Чтобы быть в большей безопасности в моем предложении containsWhere, позвоните, вы можете сделать if ([obj respondsToSelector:@selector(integerValue)]) { return [obj integerValue] == [@2 integerValue]; } else { NSLog(@"Element %@ from array to analyze doesn't responds to integerValue -> Skipped", obj); return FALSE; }, иначе вы можете получить unrecognized selector sent to ошибку сбоя.

Larme 19.06.2024 15:25

@Larme OOH Понятно... спасибо за разъяснения по поводу типа массива. Мне пришлось внести изменения в наше приложение Objective-c, но я в этом очень новичок. Так что, я думаю, проблема может заключаться в компиляции API с swaggerм. Ребята из серверной части сказали мне, что они отправляют длинные значения, а не строки, и проверяют полученный json для компиляции API, параметр apiArray равен "apiArray":{"type":"array","items":{"type":"integer","format‌​":"int64"}}.

Vincenzo 19.06.2024 15:28

Используйте xml: в нем нет проблем с числами в строках

Cy-4AH 20.06.2024 10:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
13
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В Objective-C NSArray<NSNumber*>* apiArray будет содержать только apiArray не потому, что он объявлен как таковой NSNumber:

NSArray<NSNumber*>* apiArray;
NSArray *t = @[@[], @"", @2];
apiArray = t;`

Это скомпилируется нормально, а элементами apiArray являются массив, строка и число. Это распространенная причина сбоя unrecognized selector sent to instance.

NSArray<NSNumber*>* apiArray — это всего лишь маленький помощник: Он сообщает вам, что apiArray ДОЛЖЕН содержать только NSNumber элементы (даже если это не так), поэтому, когда вы его заполняете, заполняйте его соответствующим образом, а при извлечении из него элементов он будет ожидать, что они будут NSNumber. Итак, NSArray *first = [apiArray firstObject]; выдаст предупреждение Incompatible pointer types initializing 'NSArray *' with an expression of type 'NSNumber * _Nullable', но это всё, просто предупреждение. И в моем случае первый элемент на самом деле — это NSArray, а не NSNumber.

После нескольких отзывов с вашей стороны кажется, что серверная часть отправляет NSString вместо NSNumber. Вам решать, менять его при анализе JSON или нет, или изменить локальную модель на localObject, чтобы она была NSString.

Если вы хотите сохранить оба API/local как таковые, поскольку у вас есть NSString и NSNumber для сравнения, возможный способ — использовать integerValue.

Это немного более подробно, но это должно помочь:

NSInteger index = [self indexOfObjectPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    return [obj integerValue] == [localObject integerValue];
}];
BOOL contains = index != NSNotFound;

Если вы хотите создать contains(where:) (в более «быстром» коде), вы можете создать категорию в NSArray:

NSArray+Custom.h

@interface NSArray<ObjectType> (Addon)
-(BOOL)containsWhere:(BOOL (^)(id obj))predicate;
@end

NSArray+Custom.m

@implementation NSArray (Addon)

-(BOOL)containsWhere:(BOOL (^)(id obj))predicate
{
    NSInteger index = [self indexOfObjectPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        return predicate(obj);
    }];
    return index != NSNotFound;
}
@end

Затем в использовании:

NSNumber *localObject = @2;
BOOL contains = [array containsWhere:^BOOL(id  _Nonnull obj) {
    if ([obj respondsToSelector:@selector(integerValue)]) {
        return [obj integerValue] == [localObject integerValue];
    } else {
        NSLog(@"Element %@ from array to analyze doesn't respond to integerValue -> Skipped", obj);
        return FALSE;
    }
}];
NSLog(@"%@ contains localObject: %d", array, localObject, contains);

Ух ты! Большое спасибо за ответ, мне очень приятно такое подробное объяснение. Я еще раз проверил с ребятами из серверной части, и они действительно отправляют массив строк ... хотя этого не должно быть, и они изучают это, потому что этого не должно быть, глядя на yaml OpenAPi, они используем для генерации конечных точек

Vincenzo 19.06.2024 17:03

Тем временем я просто искал расширение для NSArray, чтобы решить проблему, поскольку мое решение цикла for — не самый элегантный способ ее решения, поэтому еще раз спасибо за огромную помощь.

Vincenzo 19.06.2024 17:06

Другие вопросы по теме