2013-06-20 3 views
0

The problematic application, simplified디버그와 릴리스 구성 간의 블록 동작이 다릅니다.

내 프로그램이 완벽하게 작동합니다. 내 인생에 너에게 보증 해, 0 벌레. 자랑스럽게, 나는 TestFlight를 사용하여 베타 테스터에 ad-hoc 배포를위한 .ipa 파일로 애플리케이션을 패키징하려고 시도했다.

프로그램이 작동하지 않았습니다. 일어날 일이없는 애니메이션은 절대로 일어나지 않습니다. 네트워크 코드가 끊어집니다. 음악을 아름답게 사라지게하는 버튼은 아무 것도하지 않았습니다.

범인은 새롭고 빛나는 블록 인 것으로 나타났습니다. 시뮬레이터 나 장치에서 프로그램을 테스트 할 때 기본 "디버그"빌드 구성을 사용했습니다. 그러나 배포를 위해 보관하면 (그리고 나중에 App Store에 제출할 것을 믿습니다), XCode는 "릴리스"라는 또 다른 구성을 사용합니다. 추가로 조사한 차이점은 최적화 수준 (Xcode의 빌드 설정에서 찾을 수 있음) 때문입니다. 디버그는 없음 (-O0)을 사용하지만 릴리스는 가장 빠름, 최소 (-O)를 사용합니다. 나는 그것이 가장 빠르다, 가장 작다, 그리고 일하지 않는다는 것을 거의 알지 못했다. 예, 블록은 두 가지 구성에서 다르게 동작합니다.

그래서 문제를 해결하기 위해 착수했습니다. 세상에 곧 바뀌는 앱을 본인의 뼈에 단순화했습니다.이 게시물에 첨부 된 이미지에 나와 있습니다. View Controller는 초기 값이 0 인 인스턴스 변수 x를가집니다. b를 누르면 x가 1이 될 때 맨 아래 레이블을 변경하면서 x의 값을 계속 확인할 스레드를 생성합니다. x 값을 변경할 수 있습니다 에이. 여기

(나는 BTW ARC를 사용하고 있습니다) 내 순진 코드 :

@implementation MBIViewController 
{ 
    int _x; 
} 

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

- (void)updateLabel 
{ 
    self.topLabel.text = [NSString stringWithFormat:@"x: %d", _x]; 
} 

- (IBAction)buttonAPressed:(id)sender { 
    _x = 1; 
    [self updateLabel]; 
} 

- (IBAction)buttonBPressed:(id)sender { 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     while (_x != 1) { 
      // keep observing for value change 
     } 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      self.bottomLabel.text = @"b changed me becase x changed!"; 
     }); 
    }); 
} 

@end 

_x는 인스턴스 변수이므로 블록 포인터를 "자기"를 사용하여 액세스 할 것이라고 생각하는 것이 합리적이다 로컬 복사본이 아닙니다. 디버그 구성에서 작동합니다!

그러나 릴리스 빌드에서는 작동하지 않습니다. 아마 그 블록은 로컬 복사본을 사용하고있을 것입니다. 자, 명시 적으로 자체를 사용합시다.

while (self->_x != 1) { 
    // keep observing for value change 
} 

릴리스에서 작동하지 않습니다. 좋아요, 포인터를 사용하여 직접 변수에 접근 해 봅시다 :

int *pointerToX = &_x; 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    while (*pointerToX != 1) { 
     // keep observing for value change 
    } 
    // other codes 
}); 

여전히 작동하지 않습니다. 이것은 똑똑한 최적화 컴파일러가이 멀티 스레드 세계에서 비교 결과가 바뀔 가능성이 없다고 가정하기 때문에 항상 TRUE 또는 다른 부두가되도록 대체했을 것입니다.

내가이를 사용할 때

지금, 상황이 다시 작업을 시작 :

- (int)x 
{ 
    return _x; 
} 

: 그리고

while (_x != 1) { 
    // keep observing for value change 
    NSLog(@"%d", _x); 
} 

을 그래서 컴파일러는 비교를 최적화 우회, 나는 게터를 만들기 위해 의지 해당 게터를 사용하여 값을 확인하십시오 :

while (self.x != 1) { 
    // keep observing for value change 
} 

이제 self.x는 실제로 ca입니다. 컴파일러는 실제로 함수를 수행 할 수 있도록 충분히 정중합니다. 그러나, 나는 이것이 매우 단순한 일을하는 다소 복잡한 방식이라고 생각한다. "블록 내부의 가치 변화를 관찰"하는 작업에 직면 한 경우 다른 방법으로 코드화했을 것입니다. 또 다른 패턴을 사용할 것입니까? 고마워요!

답변

1

변수를 사용하고 루프에서 수정하지 않으면 컴파일러 최적화로 인해 변수에 대한 실제 액세스가 최적화 될 수 있습니다. 이는 컴파일 타임에 미리 계산할 수 있기 때문입니다.

이 문제를 방지하려면 컴파일러가 이러한 유형의 최적화를 적용하지 못하게하는 "volatile"키워드를 사용할 수 있습니다.

getter 및 setter에서 작동합니다. 동기화 지점 역할을하는 인스턴스에 메시지를 보내야하기 때문입니다. 로 _x 선언

+0

와우, 휘발성 키워드 작동합니다! 나는 한 번 C 책을 읽었던 것을 기억하고 결코 그것을 사용하지 않을 것이라고 상상하지 못했습니다! 감사합니다 – agro1986

+0

와우 ... 참으로 .. :) +1 : – TonyMkenu

-1

시도는 다음과 같습니다 또한 블록에 사용되는

__block int _x; 

일반적으로 변수가 복사됩니다. 이렇게하면 블록에서 _x가 수정되면 변경 내용이 외부에 표시되어야한다는 것을 컴파일러에 알립니다. 문제가 해결 될 수도 있습니다.

+0

'__block'은 지역 변수에만 적용됩니다. – newacct

관련 문제