Zastosowanie init init zamiast nowego

344

Ucząc się Celu C i czytając przykładowy kod, zauważam, że obiekty są zwykle tworzone przy użyciu tej metody:

SomeObject *myObject = [[SomeObject alloc] init];

zamiast:

SomeObject *myObject = [SomeObject new];

Czy jest ku temu powód, ponieważ przeczytałem, że są one równoważne?

willc2
źródło
12
Nawet nie wiedziałem, że to możliwe! Cały materiał, który przeczytałem, udaje, że nowe słowo kluczowe nie istnieje!
Jonathan
78
Właściwie „new” nie jest słowem kluczowym w Objective-C, ale NSObject implementuje metodę klasową „new”, która po prostu wywołuje „share” i „init”.
Don McCaughey,
3
Jonathan, właśnie to skłoniło mnie do pytania. Mogą być funkcjonalnie równoważne, ale [[przydział] init] jest wyraźnie dominującym idiomem.
willc2
1
Myślę, że Apple (z tego, co pamiętam z jednego z ich wykładów na iTunes Stanford) po prostu zachęca cię do korzystania z init init, abyś mógł zrozumieć proces tego, co się dzieje. Nie używają często nowego w swoim przykładowym kodzie, więc przydzielanie init wydaje się być tylko ogólnym dobrym nawykiem, który Apple próbuje promować.
PostCodeism

Odpowiedzi:

290

Istnieje wiele powodów: http://macresearch.org/difference-between-alloc-init-and-new

Niektóre z nich to:

  • newnie obsługuje niestandardowych inicjatorów (jak initWithString)
  • alloc-init jest bardziej wyraźny niż new

Wydaje się, że ogólna opinia jest taka, że ​​powinieneś używać wszystkiego, co ci odpowiada.

Jeremy Stanley
źródło
8
Jeśli twoja klasa używa -init jako wyznaczonego inicjatora, + new go wywoła. Jeremy ma na myśli to, że masz niestandardowy inicjator, który nie jest -init. -initWithName :, na przykład.
bbum
87
Nic nie stoi na przeszkodzie, abyś wdrożył +newWithString:, jeśli już to zrobiłeś -initWithString. Jednak nie tak często. Osobiście zawsze używam, newgdy wyznaczony inicjator jest init, po prostu taki krótki i słodki.
PeyloW
11
Wiem, że to stary wątek, ale chcę podkreślić, że nie powinieneś go wdrażać +newWithString:. To zrywa podział problemów. Bo kiedy i tak chcesz po prostu użyć -init, nie ma powodu, by po prostu tego nie używać +new.
Jonathan Sterling
7
@JathanathanSterling: Apple ma wiele przypadków, w których wydaje się, że robią dokładnie to, czego odradzasz; na przykład [[NSString alloc] initWithFormat:...]i [NSString stringWithFormat:...]oba są równoważne. Czy twierdzisz, że Apple naruszył podział problemów i nie powinien był tego w ten sposób wdrażać? (Uwaga: nie próbuję być protekcjonalny; chciałbym tylko uzyskać więcej informacji i wiedzieć, czy podążanie za przykładem Apple jest czasem złym pomysłem.)
Rozsądny
6
@Senseful Uważam, że sięga czasów sprzed ARC (lub wywozu śmieci). Te dwie rzeczy nie są równoważne. stringWithFormat: zwróci ciąg automatycznie wydany podczas przydzielania: init: będzie musiał zostać ręcznie zwolniony lub automatycznie wydany, albo spowoduje wyciek pamięci.
msfeldstein,
139

Bardzo stare pytanie, ale napisałem przykład dla zabawy - być może okaże się przydatny;)

#import "InitAllocNewTest.h"

@implementation InitAllocNewTest

+(id)alloc{
    NSLog(@"Allocating...");
    return [super alloc];
}

-(id)init{
    NSLog(@"Initializing...");
    return [super init];
}

@end

W głównej funkcji obie instrukcje:

[[InitAllocNewTest alloc] init];

i

[InitAllocNewTest new];

skutkuje tym samym wynikiem:

2013-03-06 16:45:44.125 XMLTest[18370:207] Allocating...
2013-03-06 16:45:44.128 XMLTest[18370:207] Initializing...
Guitar_freak
źródło
5
Doskonała odpowiedź! Po prostu chcę dodać, że użycie + new jest świetne w przypadkach, w których wystarczy użyć -init i nic bardziej wyspecjalizowanego jak -initWithSomething: ...
cgossain
Niestety ten przykład nie dowodzi, że są one identyczne. To tylko przykład, w którym dają taki sam wynik.
Vince O'Sullivan
10
Niestety ten przykład nie dowodzi, że są one identyczne. To tylko przykład, w którym się zdarzają, dają ten sam wynik. Z drugiej strony dowodzi to, że są one różne ... Biorąc powyższy przykład, zmodyfikuj plik „InitAllocNewTest.h” w następujący sposób: @interface InitAllocNewTest: NSObject - (instancetype) __unavailable init; @end [[InitAllocNewTest alloc] init]nie będzie się kompilować, dopóki nie będzie to miało [InitAllocNewTest new]wpływu. (Przepraszamy za brak przerw w linii itp.)
Vince O'Sullivan
1
@ VinceO'Sullivan, dobry punkt. Oznacza to, że jeśli chcemy, aby init był niedostępny, tak jak w klasie singleton, należy również wyłączyć nowy. Nie będę tutaj dotykać, czy singletony są dobre czy złe.
user3099609
Mam pytanie z powyższego kodu. dlaczego „return [super init];”? generalnie init to metoda inicjowania członków klasy, dlatego zmiany wskaźnika są bardzo dziwne!
Pruthvidhar Rao Nadunooru
52

+newjest równoważny +alloc/-initz NSObjectimplementacją Apple . Jest mało prawdopodobne, aby to się kiedykolwiek zmieniło, ale w zależności od poziomu paranoi dokumentacja Apple +newwydaje się pozwalać na zmianę implementacji (i przełamanie równoważności) w przyszłości. Z tego powodu, ponieważ „jawność jest lepsza niż domniemanie” i dla ciągłości historycznej społeczność Celu-C zasadniczo unika +new. Zazwyczaj można jednak zauważyć niedawnych użytkowników Java w Objective-C dzięki ich upartemu użyciu +new.

Barry Wark
źródło
8
Chociaż jest to często prawdą, istnieje również obóz na korzyść zwięzłości, w którym to przypadku jest bardziej wyraźny, gdy istnieje wyraźna i aktualna obawa, która uzasadnia to.
Peter DeWeese
27
Głosowałem za tym, ponieważ +newistnieje już od czasów NeXT. Jeśli coś +newjest oznaką kogoś, kto nauczył się objc dawno temu; Widzę wielu ludzi, którzy lubią język, a nawet piszą go od lat, ale wyraźnie po boomie na iOS, nie mają pojęcia, co +newto znaczy. Po drugie, jak +newto jest bardzo stare i od czasów NeXT, Apple byłoby całkiem szalone, aby zmienić go w sposób, który łamie stary kod, zwłaszcza biorąc pod uwagę, że ich własne podstawy kodu są prawdopodobnie zaśmiecone.
asveikau
1
Jestem prawie pewien, że ten newidiom pochodzi z Smalltalk. Jest również używany w Ruby, a zarówno Objective-C, jak i Ruby czerpią wiele ze swojej składni i konwencji z Smalltalk.
Brendon Roberto,
3
Pozwala tylko na zmianę alokacji. Dokumenty jawnie stan -init zostaną wywołane. Więc to zależy od tego, czy kiedykolwiek nadpiszesz alokację.
Kendall Helmstetter Gelner
7
Naprawdę nie wyobrażam sobie, dlaczego rozwiązanie, w którym musisz wpisać WIĘCEJ, byłoby preferowane (przydzielanie init), szczególnie w tak pełnym języku języku, jakim jest Objective-C, w którym oszczędzanie naciśnięć klawiszy oszczędza czas. Ci „uparci programiści Java” po prostu wykorzystują swoje umiejętności analityczne, aby spędzać mniej czasu na pisaniu kodu zamiast niepotrzebnego wpisywania dodatkowych znaków, które dają ten sam wynik funkcjonalny.
DiscDev
9

Często będziesz musiał przekazywać argumenty, initwięc będziesz używał innej metody, takiej jak [[SomeObject alloc] initWithString: @"Foo"]. Jeśli jesteś przyzwyczajony do pisania tego, masz w zwyczaju robić to w ten sposób, więc [[SomeObject alloc] init]może to być bardziej naturalne [SomeObject new].

Brian Campbell
źródło
7

Jedna krótka odpowiedź to:

  1. Oba są takie same. Ale
  2. „nowy” działa tylko z podstawowym inicjatorem „init” i nie będzie działał z innymi inicjatorami (np. initWithString :).
użytkownik739711
źródło
3

Na marginesie, osobiście używam, [Foo new]jeśli chcę, aby coś w init zostało wykonane bez użycia jego wartości zwracanej w dowolnym miejscu. Jeśli [[Foo alloc] init]nigdzie nie skorzystasz ze zwrotu , otrzymasz ostrzeżenie. Mniej więcej używam [Foo new]dla oka.

evdude100
źródło
3

Spóźniłem się z tym, ale chcę wspomnieć, że to nowe jest w rzeczywistości niebezpieczne w świecie Obj-C ze Swift. Swift utworzy domyślną metodę inicjowania tylko wtedy, gdy nie utworzysz żadnej innej inicjalizacji. Wywołanie nowego w szybkiej klasie z niestandardowym inicjatorem spowoduje awarię. Jeśli użyjesz funkcji replace / init, kompilator prawidłowo narzeka, że ​​init nie istnieje.

MurderDev
źródło
1

Jeśli new wykona za ciebie zadanie, spowoduje to również, że twój kod będzie nieco mniejszy. Jeśli inaczej byś zadzwonił[[SomeClass alloc] init] w wielu różnych miejscach kodu, utworzysz Hot Spot w implementacji new - czyli w środowisku wykonawczym objc - co zmniejszy liczbę braków w pamięci podręcznej.

W moim rozumieniu, jeśli potrzebujesz użyć niestandardowego użycia inicjalizatora [[SomeClass alloc] initCustom] .

Jeśli nie, użyj [SomeClass new].

Mike Crawford
źródło
1
Kłóciłbym się z „jeśli potrzebujesz użyć niestandardowego inicjalizatora, użyj [[SomeClass przydziel] initCustom]”. Jeśli potrzebujesz niestandardowego inicjalizatora bez żadnych parametrów, nigdy nie rób czegoś, co sugerujesz, po prostu zastąp domyślną initfunkcję i użyj jej. [[SomeClass alloc] init];Jeśli potrzebujesz parametrów, nadal nigdy nie rób czegoś takiego [[SomeClass alloc] initWith:...];. Na koniec, jeśli zastąpisz initfunkcję niestandardową implementacją, możesz wywołać obiekt newpo utworzeniu i nadal będzie on wywoływał niestandardową initimplementację.
dirtydanee