сообщение отправлено на проблему с освобожденным экземпляром

Я отлаживал следующую проблему последние пару часов и не могу прийти к правильному выводу.

- [NSIndexPath row]: сообщение отправлено освобожденному экземпляру 0x506cf90

Я реализую распознаватель смахивания для каждой ячейки в UITableView. Всякий раз, когда обнаруживается смахивание, будет вызываться приведенный ниже код. Что он делает, так это показать / скрыть подвид в ячейке. Если подпредставление уже отображается, оно скрывается, в противном случае - отображается. Если подпредставление уже отображается в ячейке 5, а в ячейке 3 обнаружено смахивание, мы сначала удаляем это подпредставление, а затем добавляем подвид в ячейку 3.

Я попробовал приведенный ниже код, и, похоже, он работает с некоторыми ячейками. Однако в нижней части UITableView есть ячейка, в которой, если я попытаюсь скрыть подпредставление, мне будет выдана ошибка, указанная выше. Он отлично работает с другой ячейкой, только с ячейками внизу. Почему это?

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

- (void)swipe:(UISwipeGestureRecognizer *)recognizer direction:(UISwipeGestureRecognizerDirection)direction
{
    if (recognizer && recognizer.state == UIGestureRecognizerStateEnded)
    {
        // Get the table view cell where the swipe occured
        CGPoint location = [recognizer locationInView:self.table];
        NSIndexPath* indexPath = [self.table indexPathForRowAtPoint:location];
        ConvoreCell* cell = (ConvoreCell *) [self.table cellForRowAtIndexPath:indexPath];

        [self.table beginUpdates];

        NSLog(@"ROW is %d", global.row);
        //removing the options view at the other cell before adding a new one
        if (global != nil && global.row != indexPath.row){
            [sideSwipeView removeFromSuperview];
            [sideSwipeView release];
            sideSwipeView = nil;
        }

        //options already exist, we need to remove it
        if (sideSwipeView != nil){
            [sideSwipeView removeFromSuperview];
            [sideSwipeView release];
            sideSwipeView = nil;
            slide = NO;
        } else {
            //options do not exist and therefore we need to add it
            NSArray * buttonData = [[NSArray arrayWithObjects:
                                     [NSDictionary dictionaryWithObjectsAndKeys:@"Mark Read", @"title", @"mark.png", @"image", nil],
                                     [NSDictionary dictionaryWithObjectsAndKeys:@"Track", @"title", @"play.png", @"image", nil],
                                     [NSDictionary dictionaryWithObjectsAndKeys:@"Leave", @"title", @"delete.png", @"image", nil],
                                     nil] retain];

            NSMutableArray * buttons = [[NSMutableArray alloc] initWithCapacity:buttonData.count];
            sideSwipeView = [[UIView alloc] initWithFrame:CGRectMake(0, cell.frame.size.height-25, 320, 25)];
            [sideSwipeView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
            [sideSwipeView setBackgroundColor:[UIColor colorWithPatternImage: [UIImage imageNamed:@"dotted-pattern.png"]]];
            [sideSwipeView setTag:-10];

            CGFloat leftEdge = BUTTON_LEFT_MARGIN;
            for (NSDictionary* buttonInfo in buttonData)
            {
                if (!([[buttonInfo objectForKey:@"title"] isEqualToString:@"Mark Read"] && [[[groups objectAtIndex:indexPath.row] unread] intValue] == 0))
                {

                    UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];

                    button.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

                    UIImage* buttonImage = [UIImage imageNamed:[buttonInfo objectForKey:@"image"]];
                    if ([[[groups objectAtIndex:indexPath.row] tracked] intValue] == 1 && [[buttonInfo objectForKey:@"title"] isEqualToString:@"Track"]){
                        buttonImage = [UIImage imageNamed:@"pause.png"];
                        [button setSelected:YES];
                    } else 
                        [button setSelected:NO];

                    button.frame = CGRectMake(leftEdge, 0, buttonImage.size.width, buttonImage.size.height);

                    UIImage* grayImage = [self imageFilledWith:[UIColor colorWithWhite:0.9 alpha:1.0] using:buttonImage];
                    [button setImage:grayImage forState:UIControlStateNormal];

                    if ([[buttonInfo objectForKey:@"title"] isEqualToString:@"Mark Read"]){
                        [button addTarget:self action:@selector(markRead:) forControlEvents:UIControlEventTouchUpInside];
                    } else if ([[buttonInfo objectForKey:@"title"] isEqualToString:@"Track"]){
                        [button addTarget:self action:@selector(track:) forControlEvents:UIControlEventTouchUpInside];
                    } else if ([[buttonInfo objectForKey:@"title"] isEqualToString:@"Leave"]){
                        [button addTarget:self action:@selector(leave:) forControlEvents:UIControlEventTouchUpInside];
                    }
                    [button setTag:indexPath.row];
                    [buttons addObject:button];

                    [sideSwipeView addSubview:button];
                    leftEdge = leftEdge + buttonImage.size.width + BUTTON_SPACING;
                }
            }

            [cell.contentView addSubview:sideSwipeView];
            [buttons release];
            [buttonData release];
            global = indexPath;
            slide = YES;

        }
        [self.table endUpdates]; 
        [self.table deselectRowAtIndexPath:indexPath animated:YES];        

    }
}

Теперь проблема в том, что указатель на indexPath каким-то образом освободился. Я смог обойти это, сделав копию indexPath вместо простой ссылки на указатель. Так я и сделал:

global = [indexPath copy];

по какой-то причине после вызова этого метода он освобождает indexPath, и я не уверен, кто это делает ... Я думаю, что это делает iOS ... это правда?


person adit    schedule 29.05.2011    source источник


Ответы (1)


    NSIndexPath* indexPath = [self.table indexPathForRowAtPoint:location];

Это возвращает автоматически выпущенный экземпляр. Итак, да, ваше назначение global должно сохранить объект. copy фактически возвращает сохраненный экземпляр.

Однако не протекайте. Если вы просто сделаете:

global = [indexPath copy];

И вы не выпускаете сначала исходное значение global, вы просочитесь.

Да, и еще: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html.

person bbum    schedule 29.05.2011
comment
‹Pedantic› indexPathForRowAtPoint: не обязательно возвращает автоматически выпущенный экземпляр (я не уверен, что это действительно так). Он возвращает бесхозный экземпляр. Эта ошибка доставляет удовольствие в определенных ситуациях. objectAtIndex: затем removeObjectAtIndex: например. ‹/Pedantic› - person Joshua Weinberg; 29.05.2011
comment
так что, прежде чем делать копию, мне нужно сделать глобальный выпуск? - person adit; 29.05.2011
comment
на всякий случай, да. В противном случае все, что раньше было в глобальном масштабе, выйдет из строя. - person Joshua Weinberg; 29.05.2011
comment
мне это кажется странным, поэтому перед копированием счетчик памяти для global равен 0, затем вы отпускаете его ... затем он переходит в -1 ... затем вы делаете копию, он переходит в 0? - person adit; 29.05.2011
comment
Педантацизм Джошуа абсолютно правильный .... @adit Не не смотрите на абсолютный счетчик удержания. Это бесполезно и деталь реализации (скорее всего, -1 означает синглтон). - person bbum; 29.05.2011