iOS (Objective-C). Срив на приложението при получаване на блок от масив

Имате въпрос относно блокове в object-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
За ARC ли говорим?   -  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. Решението е да изпратите съобщение "copy" до блока, което ще го копира в купчината и блокът няма да бъде освободен.

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
Аз също актуализирах кода. Трябва повече информация. Използвате ли dispatch за изпращане на заден план? Какво е object_1 и как е неговият гетер? - person Onik IV; 03.03.2015