Исключение при удалении строки из табличного представления. iOS Objective-C

#ios #objective-c #tableview

#iOS #objective-c #просмотр таблицы

Вопрос:

Я пытаюсь удалить строку из табличного представления. У меня есть массив, который содержит объекты для отображения (статьи). Я делаю все так, как мне сказали в руководствах. Я удаляю строку, затем удаляю соответствующий массив формы объекта, но всегда получаю выполнение. Я пробовал решения для подобных случаев, но они не смогли решить проблему. Я застрял с этой проблемой уже 3-й день. Надеюсь, вы, ребята, сможете помочь. Спасибо. Я добавлю любую дополнительную информацию или код по мере необходимости.

РЕДАКТИРОВАТЬ: возможно, я нашел причину проблемы. Я импортирую новые статьи при каждом входе в систему с внешнего сервера. При каждом входе в систему я отправляю дату последней добавленной статьи, и в ответ я получаю все статьи, которых нет в моей базе данных. Вы увидите это в коде. Я предполагаю, что self.article обновляется полученными данными только после удаления строки. Возможно, я ошибаюсь (скорее всего :)). Надеюсь, это вообще поможет.

InboxViewController.m

     //
//  ArticleViewController.m
//  ReadLater
//
//  Created by Ibragim Gapuraev on 09/06/2014.
//  Copyright (c) 2014 Sermilion. All rights reserved.
//

#import "InboxViewController.h"
#import "LoginViewController.h"

@interface InboxViewController ()

@end

@implementation InboxViewController

@synthesize db,articles, response, jsonData;


- (NSMutableArray* ) articles
{
    if (!articles) {
        articles = [[NSMutableArray alloc] initWithCapacity:20];
    }

    return articles;
}

- (Database* ) db
{
    if (!db) {
        db = [[Database alloc] init];
    }
    return db;
}


- (void)setInboxList:(NSMutableArray* )inboxList
{
    self.articles = inboxList;
}

//---------------------------------Getting data from web-------------------------------------------//

/**
 The method will connect to a given url and send date of article that has been added at last.
 Then, connectionDidFinishLoading will receive json array of all articles, that have been added to server database after that time
 **/
- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost/nextril/index.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];

    NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    [self.db openDatabase];
    NSString* date_added = [self.db getLastArticleDate];
    [self.db closeDatabase];

    //send id of article that was added last, to server,
    //which will return json arrya of all articles with id greater then the one sent
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[date_added dataUsingEncoding:NSUTF8StringEncoding]];

    if (connection) {
        NSLog(@"viewWillAppear: Connecting to server to get data...");
    }else{
        NSLog(@"viewWillAppear: Error while connecting...");
    }


}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data
{
    response = [[NSData alloc] initWithData:data];
}

//Check if data been received
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    if(sizeof(response)>0){
        //NSLog(@"Got response from server %@", response);
        NSError* error;
        NSArray* json = [NSJSONSerialization
                              JSONObjectWithData:response //1
                              options:kNilOptions
                              error:amp;error];

        self.jsonData = [[NSMutableArray alloc] initWithArray:json];
        int count = 0;
        [self.db openDatabase];

        for (int i=0; i<self.jsonData.count; i  ) {

            NSDictionary *item = [self.jsonData objectAtIndex:i];
            NSInteger article_id = [[item objectForKey:@"article_id"]integerValue];
            NSString* content = [item objectForKey:@"content"];
            NSString* author = [item objectForKey:@"author"];
            NSString* date = [item objectForKey:@"date"];

            NSString* url = [item objectForKey:@"url"];
            NSString* tags = [item objectForKey:@"tags"];
            NSInteger archived = [[item objectForKey:@"archived"]integerValue];
            NSString* title = [item objectForKey:@"title"];
            //NSLog(@"",);
            Article* article = [[Article alloc]initWithId:article_id content:content author:author date:date url:url tags:tags arhived:archived title:title];

            BOOL added = [self.db addArticleToLocalDatabase:article];
            BOOL addedToUser = [self.db addArticleToInbox:article];
            [self.articles addObject:article];
            count  ;
            if (added == true amp;amp; addedToUser == true) {
                NSLog(@"connectionDidFinishLoading: Articles has been imported. Size: %d %@", self.articles.count, self.articles);
            }else{
                NSLog(@"connectionDidFinishLoading: Failed to import article.");
            }
        }
        [self.db closeDatabase];
    }else{
        NSLog(@"connectionDidFinishLoading: Did not get resopnse from server: %@", response);
    }
    connection = nil;
}
//----------------------------------------------------------------------------------------------------



- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.db openDatabase];
    self.articles = [self.db importAllInboxArticles:16];
    [self.db closeDatabase];

    self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:self.navigationItem.rightBarButtonItem,self.editButtonItem, nil];


    //NSLog(@"Number of articles in inboxArticles %d", articles.count);
    // Do any additional setup after loading the view.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return self.articles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{



    //NSLog(@"Number of articles in articles %d", self.articles.count);
    static NSString *CellIdentifier = @"Content";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Configure the cell...
    Article* article = [self.articles objectAtIndex:indexPath.row];
    NSString *listingKey = article.title;
    NSString *listingValues = article.url;
    cell.textLabel.text = listingKey;
    cell.detailTextLabel.text = listingValues ;

    return cell;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        NSLog(@"Size of artilces: %d", self.articles.count);

        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [self.articles removeObjectAtIndex:indexPath.row];

        [tableView endUpdates];
        [tableView reloadRowsAtIndexPaths:(NSArray *)indexPath withRowAnimation:UITableViewRowAnimationFade];

    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}





@end
  

Трассировка стека:

2014-06-17 11:07:49.131 ReadLater[76572:60b] connectionDidFinishLoading: Статьи были импортированы. Размер: 4 ( «», «», «», «» ) 2014-06-17 11:07:51.078 ReadLater[76572:60b] Размер артикулов: 4 2014-06-17 11:07:51.078 ReadLater[76572:60b] * Ошибка утверждения в -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/ UIKit-2935.137 /UITableView.m: 1368 2014-06-17 11:07:51.081 ReadLater[76572:60b] * Завершение работы приложения из-за неперехваченного исключения ‘NSInternalInconsistencyException’, причина: ‘Недопустимое обновление: недопустимое количество строк в разделе 0. Количество строк, содержащихся в существующем разделе после обновления (3), должно быть равно количеству строк, содержащихся в этом разделе до обновления (3), плюс или минус количество строк, вставленных или удаленных из этого раздела (0 вставлено, 1 удалено), и плюс или минус количество строк, вставленных или удаленных из этого раздела.строк, перемещенных в этот раздел или из него (0 перемещено, 0 перемещено).’ * Стек вызовов первого броска: ( 0 CoreFoundation 0x018e61e4 exceptionPreprocess 180 1 libobjc.A.dylib
0x016658e5 objc_exception_throw 44 2 CoreFoundation
0x018e6048 [NSException raise:format:аргументы:] 136 3
Foundation 0x012454de -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] 116 4
UIKit 0x00313f63 -[UITableView _endCellAnimationsWithContext:] 13402 5 UIKit 0x00323cea -[UITableView endUpdatesWithContext:] 51 6 UIKit
0x00323d18 -[UITableViewЗавершает обновление] 41 7 ReadLater
0x00005044 -[Просмотр таблицы InboxViewController: фиксированный стиль:forRowAtIndexPath:] 484 8 UIKit
0x003356a3 -[UITableView animateDeletionOfRowWithCell:] 107 9
UIKit 0x004be595 -[UITableViewCell _swipeDeleteButtonPushed] 70 10 libobjc.A .dylib 0x01677880 -[NSObject выполняет селектор:С помощью object:С помощью object:] 77 11UIKit 0x0023d3b9 -[UIApplication sendAction:to:from:forEvent:] 108 12 UIKit
0x0023d345 -[UIApplication sendAction:toTarget:fromSender:forEvent:] 61 13 UIKit 0x0033ebd1 -[UIControl sendAction:to:forEvent:] 66 14 UIKit
0x0033efc6 -[элементов графического интерфейса _sendActionsForEvents:с:] 577 15 0x0033e243 программирования с использованием UIKit -[элементов графического интерфейса touchesEnded:с:] 641 16 программирования с использованием UIKit
0x005d32e3 _UIGestureRecognizerUpdate 17 7166 программирования с использованием UIKit
0x0027ca5a -[UIWindow _sendGesturesForEvent:] 1291 18 программирования с использованием UIKit
0x0027d971 -[UIWindow sendEvent:] 1021 19 программирования с использованием UIKit
0x0024f5f2 -[UIApplication sendEvent:] 242 20 программирования с использованием UIKit
0x00239353 _UIApplicationHandleEventQueue 11455 0x0186f77f 21 CoreFoundation __CFRUNLOOP_составляет_звонит_из_К_А_SOURCE0_выполнить_функция
15 0x0186f10b 22 CoreFoundation __CFRunLoopDoSources0 235 0x0188c1ae 23 CoreFoundation __CFRunLoopRun 910 24 CoreFoundation
0x0188b9d3 CFRunLoopRunSpecific 467 25 CoreFoundation
0x0188b7eb CFRunLoopRunInMode 123 26 GraphicsServices
0x038da5ee GSEventRunModal 192 27 GraphicsServices
0x038da42b GSEventRun 104 28 программирования с использованием UIKit
0x0023bf9b UIApplicationMain 1225 29 ReadLater
0x0000858d основной 141 30 libdyld.dylib
0x01f2d701 start 1 ) libc abi.dylib: завершение с неперехваченным исключением типа NSException (lldb)

Комментарии:

1. Перед перезагрузкой строки tableview необходимо обновить источник данных

2. Если вы посмотрите на трассировку стека, вы увидите, что ваше исключение произошло в InboxViewController, а не в ArticleViewController. Вы должны установить точку останова исключения, чтобы найти место, где происходит сбой, и опубликовать правильный фрагмент кода

3. О, моя ошибка. Да, это InboxViewController . Извините за это.

4. Из NSLogs я вижу, что в массиве статей изначально 4 записи, но в исключении указано, что их 3. Похоже, что что-то еще удаляет элемент из массива?

5. Массив NSMutable не является потокобезопасным. После получения ваших данных вы должны использовать блок отправки gcd для выполнения обновлений вашего массива в основном потоке.

Ответ №1:

Я заставил это работать!) Я установил точки останова и проверил, что происходит. Я воспользовался советом @Paulw11 и сделал свой self.articles неизменяемым и создал его изменяемую копию. Затем я решил удалить строку, которая перезагружает представление (строку), и все начало работать.

Рабочий код:

 #pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return safeArticles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    //NSLog(@"Number of articles in articles %d", self.articles.count);
    static NSString *CellIdentifier = @"Content";
    //UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    SHCTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.textLabel.backgroundColor = [UIColor clearColor];

    // Configure the cell...
    Article* article = [safeArticles objectAtIndex:indexPath.row];
    NSString *listingKey = article.title;
    NSString *listingValues = article.url;
    cell.textLabel.text = listingKey;
    cell.detailTextLabel.text = listingValues ;
    cell.delegate = self;
    cell.todoItem = article;
    return cell;
}

//// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        NSLog(@"Size of artilces: %d", safeArticles.count);

        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [safeArticles removeObjectAtIndex:indexPath.row];

        [tableView endUpdates];

        ///[tableView reloadRowsAtIndexPaths:(NSArray *)indexPath withRowAnimation:UITableViewRowAnimationFade];

    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
  

Ответ №2:

просто перезагрузите эти строки, а не всю таблицу, используя:

  - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:   (UITableViewRowAnimation)animation
  

Комментарии:

1. Я вижу, что это должно быть в коде, но, по-видимому, это не основная причина. Я все еще получаю ту же ошибку. Я добавил трассировку стека. Спасибо.