2013-02-27 7 views
2

Я следующую задачу в Objective-C с помощью НОД я не могу понять:Почему этот блок не отражает значение переменной во время создания

Я использую следующий метод для вычисления кое-что для некоторых 648 плиток. Порядок, в котором плитки обрабатываются первым, задается некоторым алгоритмом, устанавливающим переменную «pi». Переменная «загружена» является глобальной в этом контексте и начинается с 0 и правильно поднимается до 647.

Все работает отлично, когда не используется блок.

while (loaded < [self.tiles count]) { 
    long pi = /* tricky way to calculate the position index to set */; 
    NSLog(@"loaded: %d", loaded); 
    // Do this in a separate thread 
    dispatch_async(loader, ^{ 
     NSLog(@"loaded ->: %d", loaded); 
     [self.tiles[loaded] setPositionIndexTo:pi]; 
    }); 

    loaded++; 
} 

Проблема:

я получаю исключение, потому что блок пытается получить доступ к self.tiles [648]! В блочной документации Apple это означает, что значение переменной внутри блока фиксируется при создании блока, поэтому я не понимаю, как это возможно. Я действительно понимаю, что переменная, загруженная на самом деле, имеет значение 648 в конце, что должно прервать цикл без выполнения. Другая странность в том, что значение 0 также никогда не используется в блоке, но оно начинается с 1. Иногда также можно увидеть, что блок опережает цикл относительно значения загружаемого, иногда пропускает значения вообще , или несколько раз. Вот некоторые результаты:

loaded ->: 612 
loaded: 613 
loaded ->: 613 
loaded ->: 614 
loaded: 614 
loaded ->: 614 
loaded: 615 
loaded: 616 
loaded ->: 615 
loaded: 617 
loaded ->: 617 

Почему и как это возможно?

Благодарим за любые разъяснения, поскольку, как я думаю, в документах Apple четко указано, что при создании блока значение «загружено» должно быть захвачено без изменения для блока.

+0

Можете ли вы показать декларацию 'loaded' и' loader'? –

+0

он находится в частном интерфейсе класса: ** @ interface RasterMap() { int загружен; } ** ____ но я также пробовал со свойствами в каждом варианте, о котором я мог думать. –

+0

это асинхронный блок, посмотрите видео WWDC 2012 о GCD - есть именно об этом!Это нормально, потому что все асинхронные вещи перемещаются вперед (параллельно) из mainthread, и вы можете точно знать, когда именно. – iiFreeman

ответ

6

Проблема в том, что loaded является переменной экземпляра, а не локальной переменной. Блокировка захвата local scope. В Objective-C, когда вы обращаетесь к переменной экземпляра, компилятор преобразует этот доступ к доступу элемента структуры.

@interface MyClass : NSObject 
{ 
    int varA; 
} 
@end 

@implementation MyClass 
- (void)someMethod 
{ 
    varA = 42; // This 
    self->varA = 42 // is actually this after compilation 
} 
@end 

Таким образом, блок всегда будет видеть текущее значение загружается при запуске, потому что это на самом деле смотрит на self->loaded.

Существует множество решений этой проблемы, но в целом вам необходимо убедиться, что каждый блок видит уникальное значение для loaded. Легкий способ сделать это - сделать loaded локальной переменной. Если вам нужно отслеживать общий прогресс, обновите свойство прогресса из блока. (Обратите внимание, что если loader является параллельной очередью, вам необходимо быть осторожным при многопоточности при обновлении свойства прогресса. Простое решение - отправить обновление обратно в пользовательскую последовательную очередь.)

+0

Спасибо, я уже нашел ответ сам, но так как я n00b, мне не разрешают так быстро отвечать на мои вопросы;) –

1

Все стеки -статические) переменные, локальные для охватывающей лексической области, захватываются как константные переменные. Переменные экземпляра, как в вашем случае, будут доступны как обычные переменные.

Чтобы решить вашу проблему, возьмите локальную переменную со значением загруженной и используйте ее для обработки блоков.

int index = loaded; 

while (index < [self.tiles count]) { 
    long pi = /* tricky way to calculate the position index to set */; 
    NSLog(@"loaded: %d", index); 
    // Do this in a separate thread 
    dispatch_async(loader, ^{ 
     NSLog(@"loaded ->: %d", index); 
     [self.tiles[index] setPositionIndexTo:pi]; 
    }); 

    index++; 
}