2012-03-16 3 views
0

QUI 메시징/경고 시스템으로 사용할 싱글 톤 클래스를 내 응용 프로그램에 제공하기 위해 GCD를 사용하고 있습니다. 싱글 톤은 NSMutable 배열에 메시지를 추가하는 addText라는 메서드를 포함하고 배열의 각 메시지를 사용자에게 표시합니다. 다음과 같이GCD를 통한 싱글 톤 문제

#import <UIKit/UIKit.h> 

@interface Banner : UIView 

+ (Banner*) sharedBanner; 
- (id) initWithWindow:(UIWindow*)window; 
- (void) addText:(NSString*)bannerText; 
- (void) callNext; 

@end 

구현은 다음과 같습니다 :

헤더는 다음과 같습니다

문제가
#import "Banner.h" 

static Banner* sharedBanner=nil; 
static dispatch_queue_t serialQueue; 

@implementation Banner 
{ 
    @private 
    UILabel* bannerLabel; 
    NSMutableArray* textStrings; 
    BOOL triggered; 
    int counter; 

    NSTimer* timer; 
    UIButton* bannerButton; 

    CGRect defaultFrame; 
} 

+ (id)allocWithZone:(NSZone *)zone { 
    static dispatch_once_t onceQueue; 

    dispatch_once(&onceQueue, ^{ 
     serialQueue = dispatch_queue_create("MyQueue.BannerQueue", NULL); 
     if(sharedBanner == nil) 
     { 
      sharedBanner = [super allocWithZone:zone]; 
     } 
    }); 
    return sharedBanner; 
} 


+ (Banner*) sharedBanner 
{ 
    static dispatch_once_t onceQueue; 
    dispatch_once(&onceQueue, ^{ 
     sharedBanner = [[Banner alloc] init]; 
    }); 
    return sharedBanner; 
} 

- (id) initWithWindow:(UIWindow*)window 
{ 
    UIView* __block obj; 

    dispatch_sync(serialQueue,^
    { 
     obj = [super initWithFrame:CGRectMake(0, 20, window.bounds.size.width, 0)]; 

     if (obj) 
     { 
      [self setBackgroundColor:[UIColor clearColor]]; 
      defaultFrame = self.frame;  
      timer = nil; 
      counter = 0; 
      triggered = NO; 
      bannerLabel = [[UILabel alloc] initWithFrame:obj.bounds]; 
      textStrings = [[NSMutableArray alloc] init]; 
      [bannerLabel setNumberOfLines:0]; 
      [bannerLabel setLineBreakMode:UILineBreakModeWordWrap]; 
      [bannerLabel setTextAlignment:UITextAlignmentCenter]; 
      [bannerLabel setBackgroundColor:[UIColor blackColor]]; 
      [bannerLabel setTextColor:[UIColor whiteColor]]; 
      [bannerLabel setAlpha:0.55]; 
      [self addSubview:bannerLabel]; 

      bannerButton = [[UIButton alloc] initWithFrame:self.bounds]; 
      [bannerButton addTarget:self action:@selector(complete) forControlEvents:UIControlEventTouchUpInside]; 
      [bannerButton setBackgroundColor:[UIColor clearColor]]; 
      [bannerButton setEnabled:NO]; 
      [self addSubview:bannerButton]; 
    [window addSubview:self]; 
      [window bringSubviewToFront:self]; 
    } 
    }); 
    self=(Banner*)obj; 
    return self; 
} 


- (void) trigger 
{ 
triggered = YES; 
if ([textStrings count] > 0) 
{  
    [bannerLabel setText:[textStrings objectAtIndex:0]]; 
    [bannerLabel sizeToFit]; 
    [self setFrame:CGRectMake(0, self.window.bounds.size.height - bannerLabel.frame.size.height, self.window.bounds.size.width, bannerLabel.frame.size.height)]; 
    [bannerLabel setFrame:self.bounds]; 
    [bannerButton setFrame:self.bounds]; 

    [bannerButton setEnabled:YES]; 
    [UIView animateWithDuration:1 
        animations:^ 
    { 
     [bannerLabel setAlpha:1]; 
    } 
        completion:^(BOOL finished) 
    { 
     if (finished) 
     { 
      timer = [NSTimer scheduledTimerWithTimeInterval:4 
                 target:self 
                selector:@selector(fade) 
                userInfo:nil 
                 repeats:NO]; 
     } 
    }]; 
} 
else 
{ 
    triggered = NO; 
} 
} 

- (void) fade 
{ 
[bannerButton setEnabled:NO]; 
if (timer) 
{ 
timer = nil; 
} 
    [UIView animateWithDuration:1 
       animations:^ 
    { 
    [bannerLabel setAlpha:0]; 
    } 
       completion:^(BOOL finished) 
    { 
    [self setFrame:defaultFrame]; 
    [bannerLabel setFrame:defaultFrame]; 
    [bannerButton setFrame:defaultFrame]; 

    DebugLog(@"BANNER_textStrings: %@", [textStrings objectAtIndex:0]); 
    [textStrings removeObjectAtIndex:0]; 
    [self trigger]; 
}];  
} 

- (void) addText:(NSString*)bannerText 
{ 
    dispatch_sync(serialQueue,^
    { 
     [textStrings addObject:bannerText]; 

     if (!triggered) 
     { 
      [self trigger]; 
     } 
    }); 
} 

    - (void) callNext 
{ 
    counter++; 
    if (!triggered) 
    { 
    [self trigger]; 
    } 
} 

@end 

는 여러 메시지가 동시에 표시되고, 그들은, 응용 프로그램 충돌을 페이드 아웃 할 때 fade 메소드의 "removeItemAtIndex : 0"줄에.

누구든지 내가 잘못한 것을 밝힐 수 있습니까?

답변

2

이 싱글 톤의 인스턴스는 어떻게 만듭니 까? +allocWithZone 다음 -initWithWindow? 공유 인스턴스를 얻으려면 +sharedBanner으로 전화하십시오. ..하지만 +sharedBanner은 새로운 인스턴스를 생성합니까? .. 왜 allocWithZone을 가지고 있습니까? 하나만 할당하려고하지만 init 또는 initWithWindow 번을 여러 번 호출 하시겠습니까? 일단 개체를 초기화 할 수 있습니다. 이상한 alloc/init 물건에도 불구하고, 공유 인스턴스를 생성하기 위해 하나의 메소드를 호출해야하고 공유 인스턴스를 가져와야하는 경우 Singleton처럼 쓸모가 없습니다. -initWithWindow+sharedBanner에서 불러오고 싶었지만 항상 sharedBanner를 창으로 호출해야한다는 점을 깨달았습니다.하지만 그 점을 무시한 이유는 여전히 + allocWithZone을 재정의하는 이유를 설명하지 못합니다 ... 솔직히 말해서 생성하는 인스턴스 수, 시작한 인스턴스 수를 계산합니다. 스레드로부터 안전하지 않다는 것이 확실합니다.

-initWithWindow에있는 serialQueue의 사용은 어딘가에있는 끔찍한 싱글 톤에 맞을 것이라고 생각하지만 개인 큐를 사용하면 어떤 스레드가 실행될 지 모르고 모든 GUI 작업을 수행해야하는지 알 수 없다는 것을 의미합니다. 주 스레드. 그런 다음 [super initWithFrame]의 결과를 obj에 할당하고 self의 메소드를 호출합니다. 나중에 objself에 할당됩니다. 이게 신중한가요?

-trigger 직렬 대기열에있는 GUI 객체가있는 안전하지 않은 항목 (스레드 안전하지 않은 -callNext에서 호출 할 때 제외)을 수행 한 다음 어떻게 작동하는지 알 수없는 타이머를 설정합니다 runloop없이.

싱글 톤 또는 GCD 관련 항목이 필요하지 않습니다. 그것은 당신에게 커다란 해를 끼치고 있습니다. 그리고 당신이 일할 수는 있더라도 여기에서 아무 것도하지 않고 있습니다. 당신은 단순하고 빠르며 재래식이며 느리고 복잡하고 버그 투성이이며 잘 맞지 않는 것에 접근하고 있습니다. allocWithZone을 재정의 (override)하지 말고, sharedBanner를 가지지 말고, serialQueue를 가지지 않고 아무 것도 보내지 마십시오. , sync 또는 async (이 코드에서).

ViewController에는 배너의 인스턴스 변수가 있습니다. 배너의 바닐라 인스턴스 하나를 만들고 경고를 표시하려면 -addText : 메시지를 보냅니다. 모델과 같이보기 컨트롤러 또는 창에 액세스하지 않고도 어딘가에서 경고를 표시해야하는 경우 디자인을 다시 생각하고 오류 메시지를 호출 체인에 백업해야합니다. 대신 Singleton을 사용하고 UIView 하위 클래스에 종속성을 추가하십시오. -init 메소드를 작성할 때는 표준 규칙을 따르십시오. 항상 동일하며 자동으로 진행됩니다. 보기 배치 및 구성을 위해 펜촉을 사용하십시오. 보기를 창에 추가하거나 창을 앞으로 가져 오지 마십시오. 이것들은 ViewController의 작업입니다.

주 스레드에서 모두 수행하고 주 스레드에서만 상호 작용합니다. serialQueues를 사용하여 잠금을 수행해야하는 경우 GUI와 상호 작용하기 전에 레벨에서 수행하십시오.

+0

에픽 답. 그것을 사랑해! – jrturton

+0

포인트가 찍혔습니다. 나는 튜토리얼을 따르고 있었지만, 그렇게하는 것은 잘못된 것이라고 생각한다. 사과. –

+0

비록 내가 싱글 톤에 화를 냈지만 사과 할 필요는 없다. 계속 질문 해주세요 :) – hooleyhoop