Zrobiłem podklasę UIView, która ma stałą ramkę. Czy mogę po prostu zastąpić init zamiast initWithFrame:? Np.:

- (id)init {
    if ((self = [super initWithFrame:[[UIScreen mainScreen] bounds]])) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

Dokumentacja Xcode dla -initWithFrame: mówi: „Jeśli tworzysz obiekt widoku programowo, ta metoda jest wyznaczonym inicjatorem dla klasy UIView. Podklasy mogą przesłonić tę metodę, aby wykonać dowolną niestandardową inicjalizację, ale muszą wywołać super na początku ich implementacji."

Co oznacza „wyznaczony inicjator”?

38
ma11hew28 19 sierpień 2011, 23:57

3 odpowiedzi

Najlepsza odpowiedź

Wyznaczony inicjator to ten, który muszą wywołać wszystkie inne inicjatory. UIView i podklasy są nieco nietypowe, ponieważ mają dwa takie inicjatory: -initWithFrame: i -initWithCoder:, w zależności od tego, jak tworzony jest widok. Powinieneś nadpisać -initWithFrame:, jeśli tworzysz instancję widoku w kodzie, i -initWithCoder:, jeśli ładujesz go z końcówki. Lub możesz umieścić swój kod w trzeciej metodzie i nadpisać oba te inicjatory tak, aby wywołały trzecią metodę. W rzeczywistości jest to często zalecana strategia.

Na przykład możesz utworzyć podklasę UIView, ClueCharacter, która ma własną metodę inicjalizacji: -initWithPerson:place:thing:. Następnie tworzysz swój widok w ten sposób:

Obiekt-C:

ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:@"Col. Mustard"
                                                         place:kInTheStudy
                                                         thing:kTheRope];

Szybki:

var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)

W porządku, ale aby zainicjować część UIView obiektu, twoja metoda musi wywołać wyznaczony inicjator:

Obiekt-C:

-(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
{
    if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
        // your init stuff here
    }
}

Szybki:

func init(name: String, place : CluePlace, thing : ClueWeapon)
{
    if (self = super.init(CGRectMake(0, 0, 150, 200))) {
       // your init stuff here
    }
}

Jeśli chcesz wywołać inicjator swojej podklasy -init, jest to w porządku, o ile wywołasz -initWithFrame: w implementacji.

73
Steven Williamson 27 czerwiec 2015, 23:54
3
Rozumiem, że tylko jeden inicjator (wyznaczony inicjator) powinien 1) wywołać wyznaczony inicjator nadklasy i 2) zawierać logikę inicjalizacji. Wszelkie inne inicjatory powinny wywoływać wyznaczony inicjator (w tym przypadku: [self initWithPerson:nil place:nil thing:nil]). Ponadto wyznaczony inicjator nadklasy powinien zostać nadpisany, aby wywołać nowy wyznaczony inicjator.
 – 
Lee Fastenau
9 grudzień 2013, 23:58
2
Generalnie prawdą jest, że istnieje tylko jeden wyznaczony inicjator, ale nie jest to zawsze prawdziwe. Jak opisano powyżej, widok można zainicjować za pomocą -initWithFrame: lub -initWithCoder: i żadne z nich nie wywołuje drugiego. Tak więc w przypadku widoków faktycznie istnieją dwa wyznaczone inicjatory. Zobacz także dokumentację dotyczącą -initWithFrame:.
 – 
Caleb
10 grudzień 2013, 01:03
Dzięki autoukładowi i ograniczeniom po prostu stwórz własny init i wywołaj super.init(ramka: CGRect(x: 0, y: 0, szerokość: 0, wysokość: 0))
 – 
Warpzit
20 czerwiec 2016, 12:02

W UIView wywołanie [super init] jest dokładnie równe [super initWithFrame:CGRectZero]

23
Flexo 13 styczeń 2013, 23:27
1
Pamiętaj jednak, że w Swift, jeśli masz podklasę UIView z wyznaczonym inicjatorem - wywołanie super.init() spowoduje błąd kompilacji Must call a designated initializer of the superclass 'UIView', podczas gdy wywołanie super.init(frame: .zero) działa dobrze.
 – 
Nikolay Suvandzhiev
13 marzec 2017, 19:41

W klasie Objective-C z wieloma inicjatorami wyznaczony inicjator to ten, który wykonuje znaczącą pracę. Tak więc często masz klasę z kilkoma inicjatorami, powiedzmy:

- (id)initWithRect:(CGRect)someRect;

- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour;

- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
             linkTo:(id)someOtherObject;

W takim przypadku normalnie (ale nie zawsze) powiedziałbyś, że trzeci był wyznaczonym inicjatorem i zaimplementowałbyś pozostałe dwa jako np.

- (id)initWithRect:(CGRect)someRect
{
    return [self initWithRect:someRect setDefaultColour:NO];
}

- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
{
    return [self initWithRect:someRect setDefaultColour:setDefaultColour
                   linkTo:nil];
}

Jeśli klasa ma tylko jeden inicjator, to jest to wyznaczony inicjator.

W twoim przypadku, aby postępować zgodnie z najlepszymi praktykami, powinieneś wdrożyć initWithFrame:, a także waniliowy init:, który wywołuje initWithFrame: z normalnymi wymiarami. Normalna konwencja jest taka, że ​​możesz dodawać nowe odmiany init w podklasach, ale nie powinieneś ich usuwać, i że zawsze wykonujesz rzeczywistą pracę inicjującą w wyznaczonym inicjatorze. Dzięki temu wszystkie metody inicjujące z klasy nadrzędnej, których nie dostarczysz nowych implementacji, nadal będą poprawnie współpracować z twoją podklasą.

3
Tommy 20 sierpień 2011, 01:49
1
Masz rację, że wyznaczony inicjator to zazwyczaj ten z największą liczbą parametrów, ale nie musi tak być. Jeśli istnieje więcej niż jeden inicjator dla klasy, dokumentacja klasy (lub przynajmniej plik nagłówkowy) powinna wskazywać, który z nich jest wyznaczonym inicjatorem.
 – 
Caleb
20 sierpień 2011, 00:23
Właściwie myślę, że to może być przyczyną zamieszania; wskazanie, który jest wyznaczonym inicjatorem w dokumentacji, sprawia, że ​​brzmi to tak, jakbyś miał wywnioskować coś głębszego niż jesteś, gdy klasa ma tylko jeden lub dwa inicjatory. Z drugiej strony przyjęcie zasady oznaczania zawsze wyznaczonego inicjatora jest oczywiście poprawnym sposobem dokumentowania.
 – 
Tommy
20 sierpień 2011, 00:26
Nie jestem pewien, czy to śledzę… są tylko dwa sposoby, aby upewnić się, który jest wyznaczonym inicjatorem: przeczytaj kod lub przeczytaj dokumentację. Jeśli kod nie jest dostępny, dokumenty to wszystko, co masz. Może to być dobra konwencja, aby wyznaczony inicjator był tym, który ma najwięcej parametrów, ale obecnie nie jest to coś, na co można liczyć w kodzie, którego nie napisałeś.
 – 
Caleb
20 sierpień 2011, 00:31
Tak, zaktualizowałem swoją odpowiedź, ponieważ nie chciałem powiedzieć, że „większość parametrów = wyznaczony inicjator”. Mój komentarz właśnie teraz jest taki, że jeśli czytasz dokumentację klasy z pojedynczym inicjatorem, często zobaczysz, że jest ona opisywana jako wyznaczony inicjator, ponieważ ktokolwiek ją napisał, był ostrożny i dokładny. Jeśli jednak nie doceniasz tego, czym jest wyznaczony inicjator, prawdopodobnie zostaniesz zboczony z tropu, nawet jeśli nie znalazłbyś się w gorszej sytuacji, gdyby ktoś zmienił słowo „wyznaczony”. Myślę, że nie powinni, ponieważ jestem człowiekiem pasa i ortez, po prostu podkreślam.
 – 
Tommy
20 sierpień 2011, 00:40