iOS 8 работает, но iOS 7 вылетает с -[длина NSNull]: нераспознанный селектор отправлен на экземпляр
Я искал ответы на это, но все еще не смог решить, как исправить проблему. Это -[длина NSNull]: нераспознанный селектор, отправленный экземпляру, и это [длина NSNull]: нераспознанный селектор, отправленный экземпляру 0x43fe068, не помогло.
Я работаю над приложением чата с внутренним интерфейсом Parse, и у меня возникла проблема с отметкой времени, когда сообщение чата показывалось не по порядку, поэтому я удалил строки, которые вышли из строя из моей базы данных Parse, с помощью браузера данных. Когда я тестировал приложение, это, казалось, решило проблему на моем iPhone 6 Plus и на симуляторе iPhone 6 под управлением iOS 8. Однако при открытии одной и той же комнаты чата на моем iPhone 5s под управлением iOS 7 приложение вылетало последовательно следующая ошибка.
-[NSNull length]: unrecognized selector sent to instance
Я понятия не имею, почему удаление строки может вызвать это и почему только на iOS 7? Я установил точку останова для всех исключений, и вот оскорбительная строка вместе со скриншотом.
self.lastMessageLabel.textColor = [UIColor redColor];
Я все еще получаю NSNull length
сбой, даже когда я закомментирую вышеприведенную строку, но она обрывается на общем main.m.
Любые предложения о том, как решить эту проблему, будут оценены. Благодарю.
РЕДАКТИРОВАТЬ 1: Вот код из моего ChatView.m, который загружается моим PrivateInbox.
- (void)loadMessages {
if (isLoading == NO)
{
isLoading = YES;
JSQMessage *message_last = [messages lastObject];
PFQuery *query = [PFQuery queryWithClassName:PF_CHAT_CLASS_NAME];
[query whereKey:PF_CHAT_ROOM equalTo:chatroomId];
if (message_last != nil) {
[query whereKey:PF_CHAT_SENTDATE greaterThan:[self.dateFormatter stringFromDate:message_last.date]];
}
[query includeKey:PF_CHAT_USER];
[query orderByAscending:PF_CHAT_SENTDATE];
[query addAscendingOrder:PF_CHAT_CREATEDAT];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (error == nil)
{
for (PFObject *object in objects)
{
PFUser *user = object[PF_CHAT_USER];
[users addObject:user];
if(![object[PF_CHAT_TEXT] isKindOfClass:[NSNull class]]) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
JSQTextMessage *message = [[JSQTextMessage alloc] initWithSenderId:user.objectId senderDisplayName:user.objectId date:sentDate text:object[PF_CHAT_TEXT]];
[messages addObject:message];
} else if(object[PF_CHAT_PHOTO] != nil) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
PFFile* photoFile = object[PF_CHAT_PHOTO];
JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] init];
JSQMediaMessage *photoMessage = [[JSQMediaMessage alloc] initWithSenderId:user.objectId
senderDisplayName:user.objectId
date:sentDate
media:photoItem];
[messages addObject:photoMessage];
{
[photoFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
photoItem.image = [UIImage imageWithData:data];
[self.collectionView reloadItemsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForItem:[messages indexOfObject:photoMessage] inSection:0], nil]];
}];
}
} else if(object[PF_CHAT_VIDEO] != nil) {
NSDate* sentDate;
if(object[PF_CHAT_SENTDATE] != nil)
sentDate = [self.dateFormatter dateFromString:object[PF_CHAT_SENTDATE]];
else
sentDate = object.createdAt;
PFFile* videoFile = object[PF_CHAT_VIDEO];
JSQVideoMediaitem *videoItem = [[JSQVideoMediaitem alloc] initWithFileURL:[NSURL URLWithString:[videoFile url]] isReadyToPlay:YES];
JSQMediaMessage *videoMessage = [[JSQMediaMessage alloc] initWithSenderId:user.objectId
senderDisplayName:user.objectId
date:sentDate
media:videoItem];
[messages addObject:videoMessage];
}
}
if ([objects count] != 0) {
[JSQSystemSoundPlayer jsq_playMessageReceivedSound];
[self resetUnreadCount];
[self finishReceivingMessage];
}
}
else [ProgressHUD showError:@"Network error."];
isLoading = NO;
}];
}
}
РЕДАКТИРОВАТЬ 2: Я попробовал NSNullSafe от Ника Локвуда https://github.com/nicklockwood/NullSafe и это позволило открыть личную папку "Входящие" без сбоев и избавило меня от ошибки длины NSNull, но я думаю, что это просто маскирует проблему, и я все еще не понимаю не знаю, почему он не вылетел на iOS 8, но вылетел на iOS 7.
3 ответа
Я не думаю, что это может быть связано с различиями между двумя оперативными системами.
Сбой довольно очевиден, вы отправляете сообщение объекту класса NSNUll
с этим не справиться.
Тот факт, что вы используете синтаксический анализ или веб-сервисы в целом, заставляет меня думать, что этот объект был сгенерирован серверной частью как null
в формате JSON и переведен в NSNull
объект при разборе JSON.
Вы должны найти способ обработки объекта NSNull, вероятно, на уровне синтаксического анализа.
@ Андреа правильно, если вы не можете понять это со стороны API (сервера), то вот категории NSDictionary
а также NSArray
который удаляет любой NSNull
объект и ваше приложение не потерпит крах.
@interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
@end
@interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
@end
@implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = @"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSMutableDictionary dictionaryWithDictionary:[replaced copy]];
}
@end
@implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = @"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
@end
Осторожно: подходит только если вы анализируете небольшой объем данных.
Источник: /questions/28455541/zamenit-vse-obektyi-nsnull-v-nsdictionary/28455560#28455560
Два других ответа верны - это не проблема версии ОС, а то, что вам передается значение NULL, а вы его не перехватываете.
Строка, которую вы поймали в исключении:
self.lastMessageLabel.textColor = [UIColor redColor];
Указывает на симптом, а не на причину. Я не уверен на 100%, как на самом деле работает _setTextColor, но я бы сильно поспорил, что он работает, используя атрибут длины NSString/NSAttributedString, чтобы создать NSRange, чтобы он знал, где начать применять цвет и закончить применение цвета. Если данные равны NULL, то, как упоминали другие пользователи, вы пытаетесь получить доступ к атрибуту длины неправильного класса, что приводит к сбою.
Судя по трассировке стека, строки 5 (setMessage:forUser) и 6 (cellForRow...) - это то место, где вы должны пытаться поймать значение NULL. Либо так, либо вы должны изменить свой контроллер данных таким образом, чтобы при возврате значения NULL в JSON вы заменяли его заполнителем, таким как "No Message".
В любом случае ваш сбой, вероятно, происходит, когда вы присваиваете значение self.lastMessageLabel.text, когда вы строите ячейки tableView в cellForRow.... Проверьте строку NSString, которую вы используете для типа класса (isKindOfClass[NSNULL class]), и посмотрите, поможет ли это.