2012-10-30 13 views
9

szukam ogólnych wskazówek, w jaki sposób obsłużyć tej sytuacji. Oto konkretny przykład.Co się stanie, jeśli klasa nie ma udokumentowanego wyznaczonego inicjatora?

Jestem podklasy UIImageView i chcę przesłonić initWithImage, aby dodać mój własny kod inicjalizacji po sam inicjalizacji klasy super z dostarczonego obrazu.

Jednak nie ma udokumentowane wyznaczony initializer dla UIImageView, który tak bardzo klasa initializer powinienem zadzwonić do zapewnienia mój podklasą jest właściwie zainicjowana?

Jeśli klasa nie wyznacza inicjator, prawda:

  1. Załóżmy, że jest to bezpieczne, aby wywołać dowolną (UIImageView) inicjalizatorów klasy za?
  2. Spójrz na super klasy (UIView) dla wyznaczonego inicjatora?

W tym przypadku wydaje # 1 będzie odpowiedź, jak ma to sens, aby wykonać następujące czynności w moim zastąpionej inicjatora:

- (id)initWithImage:(UIImage *)image 
{ 
    self = [super initWithImage:image]; 
    if (self) { 
     // DO MY EXTRA INITIALIZATION HERE 
    } 
    return self; 
} 
+0

Wydaje mi się to w porządku. Nazywasz inicjator klasy superklasy. Jaki jest problem? Objective-C nie zapewnia mechanizmu wywoływania inicjalizatora wyznaczonej superklasy. (AFAIK) –

+0

UIImageView będzie twoją superklasą. Możesz wywołać (z 'super') dowolny poprawny inicjator tej klasy, w tym inicjatory, które mogą dziedziczyć (chociaż niektóre z nich mogą nie mieć sensu). –

+3

To rzeczywiście działa dobrze, ale moje pytanie jest bardziej ogólne. Rozumiem, że podklasa powinna zawsze wywoływać inicjator super klasy, ale skąd mam wiedzieć, co oznacza inicjator, jeśli nie jest to udokumentowane? Czy można bezpiecznie wywoływać jakiekolwiek inicjatory w super klasie, jeśli nic nie jest oznaczone jako "wyznaczone"? – techsMex

Odpowiedz

5

UIImageView ma dwa inicjatorów, więc może chcesz się upewnić, że twoja podklasa obsługuje obie te ścieżki inicjalizacji.

Można po prostu zadeklarować, że -initWithImage: jest oznaczony jako inicjator, a wszystkie inne inicjalizatory nie są obsługiwane.

Ponadto, można wdrożyć -initWithImage:highlightedImage: i wyjątek, aby wskazać, że nie jest obsługiwane.

Alternatywnie można zadeklarować -initWithImage:highlightedImage: jako wyznaczony inicjator i zadzwonić pod numer -initWithImage: do wyznaczonego inicjalizatora.

Może się okazać, że inicjator -initWithImage: jest wywoływany niezależnie od tego, czy twoja klasa została zainicjowana za pomocą -initWithImage: lub -initWithImage:highlightedImage:.

+0

Darren, stwierdziłeś, że UIImageView ma 2 inicjalizatory, ale skąd mam wiedzieć, który z nich jest inicjatorem UIImageView? czy obaj działają jako wyznaczeni inicjatorzy, jeśli żaden z nich nie jest oznaczony jako "wyznaczony"? – techsMex

+0

UIImageView nie ma wyznaczonego inicjalizatora (być może dlatego, że drugi inicjator nie został dodany przed iOS 3.0). Jeśli tak, to jest to inicjator, który zwykle zastępujesz i który obejmuje wszystkie bazy. Ale ponieważ nie ma wyznaczonego inicjalizatora, prawdopodobnie zechcesz obsłużyć oba inicjalizatory i skierować je zarówno do własnego wyznaczonego inicjalizatora. W przeciwnym razie, jeśli ktoś nieumyślnie wywoła inicjalizator, którego nie obsługuje, może to spowodować błędy, które są trudne do wyśledzenia. – Darren

+0

@Darren - OK, najwyraźniej używasz "wyznaczonego" w tym przypadku, aby oznaczać metodę "init" opisaną w specyfikacji jako "macierzystą" (mój term) innych metod "init" tej klasy. Na przykład może istnieć metoda 'initWithX:' i metoda 'initWithX: andY:'. Zwykle pierwsza (która jest metodą "wygody") wywoływała drugą, więc wystarczy zastąpić drugą metodę. Specyfikacja może lub nie może ci tego powiedzieć. Jeśli nie, musisz przesłonić wszystkie metody 'init', które * mogą * zostać wywołane podczas używania twojej klasy, ale jeśli wiesz, że tylko jedna jest używana, to po prostu ją zastąp. –

1

Każda klasa wywodząca się z NSObject ma metodę init jako jeden inicjator, który wykona proces inicjalizacji dla tego obiektu. Jeśli nie jesteś pewien, zawsze możesz użyć self = [super init] w swoim inicjalizatorze. Biorąc pod uwagę fakt, że UIImageView ma dwa inicjatorów dostarczanych przez Apple, może trzeba zastąpić oboje lub wyjątek dla użytkownika, że ​​nie potrafią korzystać z tej metody (nie zalecane).

Dla np: -

- (id)initWithCustomParam:(NSString *)param { 

    if (self = [super init]) { 
     self.myparam = param; 
    } 
    return self; 
} 

A potem można zaimplementować inne inicjatorów jak

- (id)initWithImage:(UIImage *)image { 

    if (self = [self initWithCustomParam:@"default value"]) { 
     self.image = image; 
    } 
    return self; 
} 

lub zdefiniować,

- (id)initWithImage:(UIImage *)image customParam:(NSString *)string { 

    if (self = [self initWithCustomParam:string]) { 
     self.image = image; 
    } 
    return self; 
} 
4

dokumentacja UIImageView jest straszna. Pokazuje dwa inicjalizatory, ale możesz dostać się do sytuacji, w której żaden z nich nie jest wywoływany. Na przykład używam IB i tylko initWithCoder: zostaje wywołana.

- (id)init 
{ 
    return [super init]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    return [super initWithCoder:aDecoder]; 
} 

- (id)initWithFrame:(CGRect)frame 
{ 
    return [super initWithFrame:frame]; 
} 

- (id)initWithImage:(UIImage *)image 
{ 
    self = [super initWithImage:image]; 
    return self; 
} 

- (id)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage 
{ 
    self = [super initWithImage:image highlightedImage:highlightedImage]; 
    return self; 
} 

Jedyny poprawny sposób podklasy UIImageView jest do podklasy WSZELKIE inicjatorów I każda podklasa może wywołać tylko inicjator rodzica o tej samej nazwie. Na przykład:

subclass -init można zadzwonić pod numer UIImageView -init, ale nie można zadzwonić pod numer UIImageView -initWithCoder:.

Tak, to prawdziwy ból, że nie ma wyznaczenia.

+0

to typowy problem - jeśli obiekt jest ładowany z NIB, zostanie wywołany "-initWithCoder:". Więc często też musisz to zmienić. – nielsbot

+3

Nie wierzę, że takie podejście jest bezpieczne. Ponieważ żaden z inicjalizatorów nie jest wyznaczony, nie możesz wiedzieć, że połączenie z 'super' nie spowoduje ponownego połączenia z twoim nadpisaniem innego. Jeśli nie były trywialne, możesz wykonać pracę dwa razy, a nie dwa razy. (Gdyby były trywialne, po prostu nie zaimplementowałbyś żadnego z nich.) –

+0

@KenThomases dzięki, że zaktualizowałem ten niuans –

2

Niebezpieczeństwo, gdy nie ma wyznaczonego inicjalizatora, polega na tym, że każdy inicjator, który można wywołać, może wykonywać swoją pracę w odniesieniu do jednego z innych inicjalizatorów. W takim przypadku może przypadkowo wywołać jedną z nadpisań, która nie będzie działać tak, jak powinna.

Jeśli twoja klasa ma dokładnie jeden inicjator i jest to nadpisanie inicjalizatora nadklasy, to może bezpiecznie wywołać inicjalizator, który przeszedł. Dzieje się tak, ponieważ nie ma szans, że superinicjator wejdzie (bezpośrednio lub pośrednio) ponownie w siebie, więc nie ma szansy, że ponownie wejdzie w twoje nadpisanie.

Twoja klasa może również realizować dowolną liczbę inicjalizatorów tak długo, jak żaden z nich ma taką samą nazwę jak dowolny z tych, od superklas. Ponieważ twoje imiona są unikalne, nie ma szans, że którykolwiek z inicjatorów superklasy wezwie ich przypadkowo.

1

Alternatywnym podejściem jest być leniwym. Można użyć metod takich jak viewDidLoad lub viewDidMoveToSuperview, aby wykonać niektóre ustawienia. To naprawdę zależy od tego, kiedy konfiguracja jest ważna.

+0

To wcale nie odpowiada na pytanie. –

+0

Oczywiście. To miejsce, w którym można leniwie się ustawić. – uchuugaka

Powiązane problemy