iOS (цель-C). Сбой приложения при получении блока из массива

У вас есть вопрос о блоках в Objective-C. Например, у меня есть список действий. Я инициализирую массив блоков:

self.actions =  @[
                   ^() { [self showObject:self.object_1]; },
                   ^() { [self showObject:self.object_2]; },
                   ^() { [self showObject:self.object_3]; }
];

И вызывая их, когда нажимается какая-то строка:

- (void)pressedRowAtIndex:(NSInteger)index {
    if (index < actions.count) {
        void (^action)() = [actions objectAtIndex:index];
        if (action != nil) {
            action();
        }
    }
}

И все отлично работает без проблем. Но когда я запускаю свой массив действий с помощью метода initWithObjects:

self.actions =  [NSArray alloc] initWithObjects:
                   ^() { [self showObject:self.object_1]; },
                   ^() { [self showObject:self.object_2]; },
                   ^() { [self showObject:self.object_3]; },
                       nil
    ];

Затем я получаю сбой, пытаясь получить действие по индексу, используя метод objectAtIndex класса NSArray.

Я понимаю разницу между этим inits. Первый не увеличивает количество ссылок, как это делает первый. Но может ли кто-нибудь объяснить, почему он падает?

Изменить:

Все, что я нашел. Может я нуб и где-то еще есть полезная информация. введите здесь описание изображения

В терминале нет информации о сбое: введите здесь описание изображения

Код для Onik IV:

Небольшой пример:

@interface ViewController () {
    NSArray *actions;
}

@property (nonatomic, strong) NSString *object1;
@property (nonatomic, strong) NSString *object2;
@property (nonatomic, strong) NSString *object3;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.


    actions =  [[NSArray alloc] initWithObjects:
                ^() { [self showObject:self.object1];},
                ^() { [self showObject:self.object2]; },
                ^() {[self showObject:self.object3]; },
                nil];



}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    self.object1 = @"object 1";
    self.object2 = @"object 2";
    self.object3 = @"object 3";

    void(^firsSimpleBlock)(void) = [actions lastObject];

    firsSimpleBlock();

    void(^simpleBlock)(void) = [actions firstObject];


    simpleBlock();
}

-(void)showObject:(NSString *)object
{
    NSLog(@"Show: %@",object);
}

@end

person Vasyl Khmil    schedule 03.03.2015    source источник
comment
Пожалуйста, опубликуйте сообщение об ошибке, трассировку стека и конкретную строку сбоя.   -  person Ian MacDonald    schedule 03.03.2015
comment
Мы говорим об АРК?   -  person Cy-4AH    schedule 03.03.2015
comment
@ Cy-4AH да. Как я уже сказал, я знаю разницу между этим inits.   -  person Vasyl Khmil    schedule 03.03.2015
comment
Нажмите Continue program execution несколько раз, пока исключение не будет напечатано в выводе.   -  person Cy-4AH    schedule 03.03.2015
comment
Может случиться так, что когда вы создаете блок, он создается в стеке, и когда вы добавляете что-то в массив, его счетчик сохранения будет увеличен, поэтому объект не будет удален, что приведет к указателю на парус в NSArray. Блоки, возможно, потребуется скопировать в кучу перед помещением в массив (чтобы они не удалялись при извлечении фрейма из стека). Так что попробуйте добавить блоки в массив другим способом с помощью addObject:[theBlock copy] или около того.   -  person Peter Segerblom    schedule 03.03.2015
comment
Я перечитал ваш пост и теперь еще больше уверен. При использовании литералов в Objective-C созданные там объекты размещаются в литеральном пуле и никогда не освобождаются. С другой стороны, когда вы создаете их динамически, они представляют собой отдельные объекты, размещенные в куче.   -  person Peter Segerblom    schedule 03.03.2015
comment
@PeterSegerblom и как это исправить? Новые слышали о буквальном пуле.   -  person Vasyl Khmil    schedule 03.03.2015
comment
@VasylKhmil проверьте мой ответ ниже. Синтаксис моего блока может быть неправильным... но концепция заключается в том, что вам нужно запустить копирование блоков, прежде чем помещать их в массив.   -  person Peter Segerblom    schedule 03.03.2015
comment
@VasylKhmil, я думаю, вы ищете проблему не в том месте. Пожалуйста, дайте больше информации об объекте_1.   -  person Onik IV    schedule 03.03.2015


Ответы (2)


Попробуйте что-нибудь вроде этого.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    (^someBlock)(void) = ^void(void){
        self.object1; 
    };
    actions =  [[NSArray alloc] initWithObjects:
                [someBlock copy],
                [someOtherBlock copy],
                [anotherBlock copy],
                nil];



}

Блоки размещаются в стеке и поэтому удаляются, когда кадр удаляется из стека, что приводит к появлению указателей на парус для всех указателей, указывающих на этот блок. Когда вы выделяете объект со знаком литерала "@", объект размещается в пуле, поэтому все литералы, которые являются "одними и теми же", указывают на один и тот же экземпляр и никогда не освобождаются.

NSString *a = @"A";
NSString *b = @"A";

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

NSString *a = [NSString stringWithFormat:@"A"];
NSString *b = [NSString stringWithFormat:@"A"];

это два разных объекта.

Таким образом, это работает, когда вы создаете литеральный массив, но когда вы добавляете блоки динамически, они будут удалены, когда придет время использовать их для BAD_ACCESS. Решение состоит в том, чтобы отправить сообщение «копировать» в блок, который скопирует его в кучу, и блок не будет освобожден.

person Peter Segerblom    schedule 03.03.2015
comment
объясните пожалуйста в чем проблема и почему ваш код ее решает - person Vasyl Khmil; 03.03.2015

То же самое, у вас должны быть проблемы другого рода (синтаксис?).

Попробуй это:

@interface ViewController ()

@property (nonatomic, strong) NSString *object1;
@property (nonatomic, strong) NSString *object2;
@property (nonatomic, strong) NSString *object3;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.object1 = @"object 1";
self.object2 = @"object 2";
self.object3 = @"object 3";


NSArray *actions =  @[^() { [self showObject:self.object1];},
                            ^() { [self showObject:self.object2]; },
                            ^() {[self showObject:self.object3]; }
                            ];

NSArray *secondActions = [[NSArray alloc] initWithObjects:
^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() { [self showObject:self.object3];},
nil
];

void(^firsSimpleBlock)(void) = [actions lastObject];

firsSimpleBlock();

void(^simpleBlock)(void) = [secondActions firstObject];


simpleBlock();


}

-(void)showObject:(NSString *)object
{
NSLog(@"Show: %@",object);
}

@end
person Onik IV    schedule 03.03.2015
comment
Я думаю проблема в езде на велосипеде. Все действия используют себя, а я использую действия. Но я не понимаю, почему это вызывает проблемы. - person Vasyl Khmil; 03.03.2015
comment
Я тоже обновил код. Вы должны больше информации. Вы используете диспетчер для отправки в фоновом режиме? Что такое object_1 и как работает его геттер? - person Onik IV; 03.03.2015