Jakie są najlepsze praktyki, których używasz podczas pisania Celu C i Kakao? [Zamknięte]

346

Wiem o HIG (co jest całkiem przydatne!), Ale jakich praktyk programistycznych używasz podczas pisania Objective-C, a dokładniej, gdy używasz Cocoa (lub CocoaTouch).

pikseli
źródło
zobacz ten post na blogu, bardzo fajnie. ironwolf.dangerousgames.com/blog/archives/913
user392412

Odpowiedzi:

398

Jest kilka rzeczy, które zacząłem robić, ale nie uważam ich za standardowe:

1) Wraz z pojawieniem się właściwości nie używam już znaku „_” do prefiksu zmiennych „prywatnych”. W końcu, jeśli dostęp do zmiennej mogą uzyskać inne klasy, czy nie powinna istnieć dla niej własność? Zawsze nie podobał mi się prefiks „_” powodujący, że kod był brzydszy, a teraz mogę go pominąć.

2) Mówiąc o rzeczach prywatnych, wolę umieszczać definicje metod prywatnych w pliku .m w rozszerzeniu klasy, takim jak:

#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

Po co zaśmiecać plik .h sprawami, na które osoby z zewnątrz nie powinny się przejmować? Funkcja empty () działa dla kategorii prywatnych w pliku .m i wydaje kompilowane ostrzeżenia, jeśli nie zaimplementujesz zadeklarowanych metod.

3) Zacząłem umieszczać dealloc na górze pliku .m, tuż pod dyrektywami @synthesize. Czy to, czego zwalniasz, nie powinno znajdować się na szczycie listy rzeczy, o których chcesz pomyśleć w klasie? Jest to szczególnie prawdziwe w środowisku takim jak iPhone.

3.5) W komórkach tabeli, aby każdy element (w tym sama komórka) był nieprzezroczysty dla wydajności. Oznacza to ustawienie we wszystkim odpowiedniego koloru tła.

3.6) Podczas korzystania z NSURLConnection z reguły możesz chcieć zaimplementować metodę delegowania:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
      return nil;
}

Uważam, że większość połączeń internetowych jest bardzo pojedyncza i jest to raczej wyjątek niż reguła, że ​​będziesz chciał buforować odpowiedzi, szczególnie w przypadku połączeń z usługami internetowymi. Implementacja przedstawionej metody wyłącza buforowanie odpowiedzi.

Interesujące są także dobre wskazówki Josepha Mattiello dotyczące iPhone’a (otrzymane na liście mailingowej iPhone'a). Jest ich więcej, ale były to najbardziej przydatne, jak myślałem (zwróć uwagę, że kilka bitów zostało nieco zmodyfikowanych w stosunku do oryginału, aby zawierały szczegóły oferowane w odpowiedziach):

4) Używaj podwójnej precyzji tylko w razie potrzeby, na przykład podczas pracy z CoreLocation. Upewnij się, że kończysz swoje stałe literą „f”, aby gcc zapisał je jako zmiennoprzecinkowe.

float val = someFloat * 2.2f;

Jest to szczególnie ważne, gdy w someFloatrzeczywistości może być podwójna, nie potrzebujesz matematyki w trybie mieszanym, ponieważ tracisz precyzję w „val” podczas przechowywania. Chociaż liczby zmiennoprzecinkowe są obsługiwane sprzętowo na iPhone'ach, wykonywanie arytmetyki podwójnej precyzji może zająć więcej czasu niż pojedynczej precyzji. Bibliografia:

Na starszych telefonach podobno obliczenia działają z tą samą prędkością, ale w rejestrach może znajdować się więcej pojedynczych elementów precyzyjnych niż podwójnych, więc w przypadku wielu obliczeń pojedyncza precyzja będzie szybsza.

5) Ustaw swoje właściwości jako nonatomic. Są atomicdomyślnie i po syntezie utworzony zostanie kod semafora, aby zapobiec problemom z wielowątkowością. 99% z was prawdopodobnie nie musi się o to martwić, a kod jest znacznie mniej rozdęty i bardziej wydajny pod względem pamięci, gdy ustawiony jest na tryb nieatomowy.

6) SQLite może być bardzo, bardzo szybkim sposobem buforowania dużych zbiorów danych. Na przykład aplikacja do mapowania może buforować swoje kafelki w plikach SQLite. Najdroższą częścią jest dysk I / O. Unikaj wielu małych zapisów, wysyłając BEGIN;i COMMIT;między dużymi blokami. Używamy na przykład 2-sekundowego timera, który resetuje się przy każdym nowym przesłaniu. Kiedy wygasa, wysyłamy COMMIT; , co powoduje, że wszystkie Twoje zapisy idą w jednym dużym kawałku. SQLite przechowuje dane transakcji na dysk. Dzięki temu zawijanie początku / końca pozwala uniknąć tworzenia wielu plików transakcji, grupując wszystkie transakcje w jeden plik.

Ponadto SQL zablokuje GUI, jeśli znajduje się w głównym wątku. Jeśli masz bardzo długie zapytanie, dobrym pomysłem jest przechowywanie zapytań jako obiektów statycznych i uruchamianie SQL w osobnym wątku. Pamiętaj, aby owinąć wszystko, co modyfikuje bazę danych dla ciągów zapytań w @synchronize() {}blokach. W przypadku krótkich zapytań wystarczy zostawić rzeczy w głównym wątku, aby ułatwić sobie obsługę.

Więcej wskazówek dotyczących optymalizacji SQLite znajduje się tutaj, choć dokument wydaje się nieaktualny, wiele punktów jest prawdopodobnie nadal dobrych;

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

Kendall Helmstetter Gelner
źródło
3
Niezła wskazówka na temat podwójnej arytmetyki.
Adam Ernst,
8
Rozszerzenia klas są teraz preferowanym sposobem dla metod prywatnych: developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/…
Casebash
9
Twoja rada na temat
gier
3
Nieaktualne; całkowicie błędne: oryginalny obsługiwany iPhone unosi się i podwaja sprzęt z mniej więcej taką samą prędkością. SQLite również nie przechowuje transakcji w pamięci; są zapisywane na dysku. Tylko długie zapytania blokują interfejs użytkownika; mniej niechlujne jest uruchamianie wszystkiego w głównym wątku i używanie szybszych zapytań.
tc.
1
@tc: Poprawiłem element SQL dotyczący transakcji, zauważ, że sam nie napisałem tych ostatnich czterech lub więcej elementów. Wyjaśniłem również, że część dotycząca przenoszenia zapytań do tła dotyczyła tylko bardzo długich zapytań (czasami po prostu nie można ich skrócić). Ale nazywanie tego wszystkiego „złym” z kilku powodów jest raczej ekstremalne. Również powyższa odpowiedź brzmiała: „Na starszych telefonach podobno obliczenia działają z tą samą prędkością”, ale zwróć uwagę na większą liczbę pojedynczych rejestrów precyzyjnych, co czyni je nadal preferowanymi.
Kendall Helmstetter Gelner,
109

Nie używaj nieznanych ciągów jako ciągów formatu

Gdy metody lub funkcje przyjmują argument ciągu formatu, należy upewnić się, że masz kontrolę nad zawartością ciągu formatu.

Na przykład podczas rejestrowania ciągów kuszące jest przekazanie zmiennej ciąg jako jedynego argumentu do NSLog:

    NSString *aString = // get a string from somewhere;
    NSLog(aString);

Problem polega na tym, że ciąg może zawierać znaki interpretowane jako ciągi formatu. Może to prowadzić do błędnych wyników, awarii i problemów z bezpieczeństwem. Zamiast tego należy zastąpić zmienną łańcuchową łańcuchem formatującym:

    NSLog(@"%@", aString);
mmalc
źródło
4
Już wcześniej to ugryzłem.
Adam Ernst,
To dobra rada dla każdego języka programowania
Tom Fobear
107

Używaj standardowych konwencji nazewnictwa i formatowania Cocoa oraz terminologii, a nie tego, do czego przywykłeś z innego środowiska. Tam dużo programistów kakao tam, a gdy drugi z nich rozpoczyna pracę z kodem, to będzie dużo bardziej przystępny jeśli wygląda i czuje się podobny do innego kodu Cocoa.

Przykłady tego, co robić, a czego nie robić:

  • Nie deklaruj id m_something;w interfejsie obiektu i nie nazywaj go zmienną składową lub polem ; użyj somethinglub _somethingdla jego nazwy i nazwij ją zmienną instancji .
  • Nie nazywaj gettera -getSomething; właściwa nazwa kakao jest po prostu -something.
  • Nie nazywaj setera -something:; powinno być-setSomething:
  • Nazwa metody jest przeplatana argumentami i zawiera dwukropki; to -[NSObject performSelector:withObject:]nie jest NSObject::performSelector.
  • W nazwach metod, parametrach, zmiennych, nazwach klas itp. Używaj inter-caps (CamelCase) zamiast podpasek (podkreślników).
  • Nazwy klas zaczynają się od dużej litery, nazwy zmiennych i metod są pisane małymi literami.

Cokolwiek robisz, nie używaj notacji węgierskiej w stylu Win16 / Win32. Nawet Microsoft zrezygnował z tego, przechodząc na platformę .NET.

Chris Hanson
źródło
5
Twierdziłbym, że w ogóle nie używaj setSomething: / coś - zamiast tego używaj właściwości. W tym momencie istnieje kilka osób, które naprawdę trzeba kierować Tiger (jedyny powód, aby nie właściwości użytkowe)
Kendall Helmstetter Gelner
18
Właściwości nadal generują dla ciebie metody akcesora, a atrybuty getter = / setter = we właściwości pozwalają ci określić nazwy metod. Ponadto można użyć składni [foo coś] zamiast składni foo.something z właściwościami. Dlatego nazwa akcesorium jest nadal aktualna.
Chris Hanson
3
To świetna wzmianka dla kogoś pochodzącego z C ++, w którym zrobiłem większość rzeczy, których odradzam.
Clinton Blackmore,
4
Seter nie powinien powodować, że coś zostanie zapisane w bazie danych. Istnieje powód, dla którego dane podstawowe mają opcję -save: metoda na NSManagedObjectContext, zamiast ustawiania generujących natychmiastowe aktualizacje.
Chris Hanson,
2
Wątpię, by nie była to opcja, jednak mogła wymagać przeglądu architektury aplikacji. (Mówiąc jasno: nie mówię: „Powinieneś był użyć danych podstawowych”. Mówię „Settery nie powinny zapisywać w bazie danych.”) Posiadanie kontekstu do zarządzania wykresem obiektów, zamiast zapisywania w nim poszczególnych obiektów , jest praktycznie zawsze możliwe i lepsze rozwiązanie.
Chris Hanson
106

IBOutlets

Historycznie zarządzanie pamięcią gniazd było słabe. Obecnie najlepszą praktyką jest deklarowanie punktów sprzedaży jako właściwości:

@interface MyClass :NSObject {
    NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end

Korzystanie z właściwości wyjaśnia semantykę zarządzania pamięcią; zapewnia również spójny wzorzec, jeśli używasz syntezy zmiennych instancji.

mmalc
źródło
1
czy ładowanie stalówki nie zachowałoby go dwa razy? (raz w stalówce, drugi przez przypisanie do właściwości). Czy mam zwolnić tych w dealloc?
Kornel
6
Musisz zerować gniazda w viewDidUnload (iPhone OS 3.0+) lub w niestandardowej metodzie setView: aby uniknąć wycieków. Oczywiście powinieneś także zwolnić w dealloc.
Frank Szczerba,
2
Należy pamiętać, że nie wszyscy zgadzają się z tym stylem: weblog.bignerdranch.com/?p=95
Michael
W ten sposób Apple też robi rzeczy. „Początek rozwoju iPhone'a 3” wspomina również o tej zmianie w stosunku do poprzednich wersji.
ustun
Wspomniałem o tym w innym komentarzu, ale powinienem był to tutaj umieścić: gdy zacznie się dynamiczna synteza ivar dla aplikacji na iOS (jeśli / kiedy?), Będziesz zadowolony, że umieściłeś IBOutlet na tej właściwości w porównaniu z ivar!
Joe D'Andrea
97

Użyj LLVM / Clang Static Analyzer

UWAGA: pod Xcode 4 jest teraz wbudowany w IDE.

Korzystasz z Clang Static Analyzer, aby - co nie dziwi - analizować kod C i Objective-C (jeszcze nie C ++) w Mac OS X 10.5. Instalacja i obsługa jest banalna:

  1. Pobierz najnowszą wersję z tej strony .
  2. Z wiersza polecenia cd do katalogu projektu.
  3. Wykonać scan-build -k -V xcodebuild .

(Istnieją pewne dodatkowe ograniczenia itp., W szczególności powinieneś przeanalizować projekt w konfiguracji „Debuguj” - patrz http://clang.llvm.org/StaticAnalysisUsage.html w celu uzyskania szczegółowych informacji - ale to mniej więcej tak do czego sprowadza się.)

Analizator tworzy następnie zestaw stron internetowych, które pokazują prawdopodobne zarządzanie pamięcią i inne podstawowe problemy, których kompilator nie jest w stanie wykryć.

mmalc
źródło
1
Miałem trochę problemów z uruchomieniem
brązowy
15
W XCode 3.2.1 na Snow Leopard jest już wbudowany. Możesz uruchomić go ręcznie, używając Uruchom -> Kompiluj i analizuj , lub możesz włączyć go dla wszystkich kompilacji za pomocą ustawienia kompilacji „Uruchom statyczny analizator”. Zauważ, że to narzędzie obecnie obsługuje tylko C i Objective-C, ale nie C ++ / Objective-C ++.
oefe
94

To jest subtelne, ale przydatne. Jeśli przekazujesz siebie jako delegata do innego obiektu, zresetuj delegata tego obiektu przed sobą dealloc.

- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}

Robiąc to, upewniasz się, że żadne metody delegowania nie zostaną wysłane. Jak masz zamiardealloc zniknąć w eterze i chcesz się upewnić, że nic nie będzie w stanie wysłać Ci więcej wiadomości przez przypadek. Pamiętaj, że self.someObject może zostać zatrzymany przez inny obiekt (może to być singleton lub pula autorelease lub cokolwiek innego) i dopóki nie powiesz mu „przestań wysyłać mi wiadomości!”, Myśli, że twój obiekt jest prawie niedozwolony jest uczciwa gra.

Przyzwyczajenie się do tego nawyku uratuje cię przed wieloma dziwnymi awariami, które trudno jest debugować.

Ta sama zasada dotyczy również Obserwacji Wartości Kluczowych, a także Powiadomień NS.

Edytować:

Jeszcze bardziej defensywne, zmień:

self.someObject.delegate = NULL;

w:

if (self.someObject.delegate == self)
    self.someObject.delegate = NULL;
schwa
źródło
8
Nie ma w tym nic subtelnego, dokumentacja wyraźnie mówi, że musisz to zrobić. Od Memory Management Programming Guide for Cocoa: Additional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates.
John
Lepiej używać zer zamiast NULL, ponieważ NULL nie zwolni pamięci.
Naveen Shan
@NaveenShan nil == NULL. Są dokładnie takie same, z wyjątkiem tego, że niljest idi NULLjest void *. Twoje stwierdzenie nie jest prawdziwe.
@WTP tak, zero == NULL, ale użycie nilu jest wyraźnie preferowanym sposobem, jeśli przejrzysz przykładowe fragmenty kodu jabłek, używają nilu wszędzie i jak powiedziałeś, zero jest identyfikatorem, co czyni go lepszym od pustki * , w przypadkach, gdy wysyłasz identyfikatory, to znaczy.
Ahti
1
@Ahti dokładnie, a Nil(wielkie litery) są typu Class*. Mimo że wszystkie są równe, użycie niewłaściwego może wprowadzić nieprzyjemne małe błędy, szczególnie w Objective-C ++.
86

@kendell

Zamiast:

@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end

Posługiwać się:

@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end

Nowości w Objective-C 2.0.

Rozszerzenia klas opisano w dokumentacji Apple Objective-C 2.0.

„Rozszerzenia klas pozwalają zadeklarować dodatkowe wymagane API dla klasy w lokalizacjach innych niż w bloku klasy podstawowej @interface”

Są więc częścią rzeczywistej klasy - a NIE kategorią (prywatną) oprócz klasy. Subtelna, ale ważna różnica.

schwa
źródło
Możesz to zrobić, ale ja
wolę nazywać
2
Z wyjątkiem nie jest różnica między prywatnymi i rozszerzeń kategoriach klasowych: „Rozszerzenia klasy pozwalają zadeklarować dodatkowe wymagane dla klasy API w miejscach innych niż w pierwotnym bloku klasy @interface, jak pokazano w poniższym przykładzie:” Patrz odnośnik w edycji.
schwa
Zgadzam się, że istnieje różnica, w której kompilator ostrzega cię, jeśli nie zaimplementowałeś metod CE - ale nie uważam tego aspektu za bardzo ważny, gdy wszystkie metody są w tym samym pliku i wszystkie są prywatne. Nadal wolę aspekt konserwacji oznaczania prywatnego bloku odniesienia do przodu
Kendall Helmstetter Gelner,
3
Naprawdę nie widzę (Prywatnego) jako bardziej łatwego do utrzymania niż (). Jeśli tak się martwisz, spora dawka komentarzy może pomóc. Ale oczywiście żyj i pozwól żyć. YMMV itp.
schwa
17
Istnieje raczej ważna zaleta do użycia ()zamiast (Private)(lub jakiejś innej nazwy kategorii): możesz ponownie ustawić właściwości jako readwrite, podczas gdy publicznie są one tylko do odczytu. :)
Pascal
75

Unikaj automatycznego wydawania

Ponieważ zazwyczaj (1) nie masz bezpośredniej kontroli nad ich czasem życia, automatycznie wydane obiekty mogą utrzymywać się przez stosunkowo długi czas i niepotrzebnie zwiększają powierzchnię pamięci aplikacji. Chociaż na pulpicie może to nie mieć większego znaczenia, na bardziej ograniczonych platformach może to być znaczący problem. Dlatego na wszystkich platformach, a zwłaszcza na platformach o ograniczonym dostępie, za najlepszą praktykę uważa się unikanie stosowania metod prowadzących do automatycznie wydanych obiektów, a zamiast tego zachęca się do korzystania ze wzoru alokacji / inicjowania.

Dlatego zamiast:

aVariable = [AClass convenienceMethod];

w miarę możliwości należy zamiast tego użyć:

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];

Pisząc własne metody zwracające nowo utworzony obiekt, możesz skorzystać konwencji nazewnictwa Cocoa aby oznaczyć odbiornik, że należy go zwolnić, dodając nazwę metody do „new”.

Dlatego zamiast:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}

możesz napisać:

- (MyClass *)newInstance {
    MyClass *instance = [[self alloc] init];
    // configure instance
    return instance;
}

Ponieważ nazwa metody zaczyna się od „nowy”, konsumenci twojego API wiedzą, że są odpowiedzialni za zwolnienie otrzymanego obiektu (patrz na przykład metoda NSObjectControllernewObject ).

(1) Możesz przejąć kontrolę, korzystając z własnych lokalnych pul automatycznych wydań. Aby uzyskać więcej informacji na ten temat, zobacz Pule automatycznego wydawania .

mmalc
źródło
6
Uważam, że korzyści wynikające z niestosowania automatycznego uwalniania przewyższają jego koszty (tj. Więcej błędów wycieków pamięci). Kod w głównym wątku i tak powinien być dość krótki (w przeciwnym razie zamrozisz interfejs użytkownika), a w przypadku dłuższego kodu w tle, który wymaga dużej ilości pamięci, zawsze możesz owinąć części wymagające dużej ilości pamięci w lokalne pule autorelease.
adib
56
Nie zgadzam się. W miarę możliwości należy używać obiektów automatycznie wydanych. Jeśli nadmiernie zwiększają powierzchnię pamięci, powinieneś użyć innej NSAutoreleasePool. Ale dopiero po potwierdzeniu, że to naprawdę problem. Przedwczesna optymalizacja i tak dalej ...
Sven
3
Spędzam mniej niż 40 sekund. dzień pisania [releaseObject some] i czytania „dodatkowej linii” podczas tworzenia nowego obiektu, ale kiedyś spaliłem przez 17 godzin, aby znaleźć błąd autorelease, który pojawiałby się tylko w szczególnych przypadkach i nie dawał spójnego błędu w konsoli. Zgadzam się więc z adibem, który ujął to w następujący sposób: „Uważam, że korzyści wynikające z niestosowania automatycznego uwalniania przewyższają koszty”.
RickiG,
7
Zgadzam się ze Svenem. Podstawowym celem powinna być przejrzystość kodu i ograniczenie błędów kodowania, a optymalizacja pamięci tylko tam, gdzie jest to potrzebne. Wpisanie autorelease [[[Foo przydzielenie] init]] jest szybkie i od razu zajmujesz się kwestią wydania tego nowego obiektu. Podczas czytania kodu nie musisz szukać odpowiedniego wydania, aby upewnić się, że nie wycieknie.
Mike Weller,
3
Cykl życia automatycznie wydanych obiektów jest dobrze zdefiniowany i możliwy do ustalenia na wystarczającym poziomie.
Eonil
69

Niektóre z nich zostały już wspomniane, ale oto, co mogę myśleć z góry:

  • Przestrzegaj zasad nazewnictwa KVO. Nawet jeśli nie korzystasz teraz z KVO, z mojego doświadczenia wynika, że ​​często jest to korzystne w przyszłości. A jeśli używasz KVO lub powiązań, musisz wiedzieć, że wszystko działa tak, jak powinno. Obejmuje to nie tylko metody akcesoryjne i zmienne instancji, ale także wiele relacji, sprawdzanie poprawności, automatyczne klucze zależne i tak dalej.
  • Umieść prywatne metody w kategorii. Nie tylko interfejs, ale także implementacja. Dobrze jest mieć koncepcyjnie pewien dystans między metodami prywatnymi i nieprywatnymi. Wszystko dołączam do pliku .m.
  • Umieść metody wątku w tle w kategorii.Tak samo jak powyżej. Odkryłem, że dobrze jest zachować wyraźną barierę konceptualną, gdy myślisz o tym, co jest w głównym wątku, a co nie.
  • Zastosowanie #pragma mark [section]. Zwykle grupuję według własnych metod, przesłonięć każdej podklasy i wszelkich informacji lub protokołów formalnych. To znacznie ułatwia przejście do dokładnie tego, czego szukam. Na ten sam temat zgrupuj podobne metody (takie jak metody delegowania widoku tabeli), nie po prostu ich nigdzie.
  • Przedrostek prywatnych metod i ivars znakiem _. Podoba mi się wygląd i mniej prawdopodobne jest, że użyję ivara, gdy mam na myśli przypadkową nieruchomość.
  • Nie używaj metod / właściwości mutatora w init i dealloc. Nigdy nie miałem z tego powodu nic złego, ale widzę logikę, jeśli zmienisz metodę, aby zrobić coś, co zależy od stanu twojego obiektu.
  • Umieść IBOutlety we właściwościach. Właśnie przeczytałem to tutaj, ale zacznę to robić. Niezależnie od jakichkolwiek korzyści z pamięci, wydaje się lepiej stylistycznie (przynajmniej dla mnie).
  • Unikaj pisania kodu, którego absolutnie nie potrzebujesz. To naprawdę obejmuje wiele rzeczy, takich jak tworzenie ivarów, kiedy to #definezrobi, lub buforowanie tablicy zamiast sortowania jej za każdym razem, gdy potrzebne są dane. Mogę o tym wiele powiedzieć, ale najważniejsze jest to, że nie pisz kodu, dopóki go nie potrzebujesz, bo inaczej profiler ci to nakazuje. Na dłuższą metę znacznie ułatwia utrzymanie.
  • Dokończ to, co zaczynasz. Mając wiele niedokończonych, błędny kod jest najszybszym sposobem na zabicie projektu martwego. Jeśli potrzebujesz metody pośredniczącej, która jest w porządku, po prostu wskaż ją, wkładając do NSLog( @"stub" )środka, lub w inny sposób chcesz śledzić różne rzeczy.
Marc Charbonneau
źródło
3
Sugerowałbym, abyś umieścił prywatne metody w kontynuacji zajęć. (tj. @interface MyClass () ... @end w twoim .m)
Jason Medeiros
3
Zamiast #PRAGMA możesz użyć komentarza // Znak: [Sekcja], który jest bardziej przenośny i działa identycznie.
aleemb
O ile nie ma specjalnej składni, której mi brakuje, // Mark: nie dodaje etykiety w menu rozwijanym funkcji Xcode, co jest tak naprawdę połową powodu jej używania.
Marc Charbonneau,
6
Musisz użyć wielkich liter „// MARK: ...”, aby pojawił się w menu rozwijanym.
Rhult,
3
W odniesieniu do Finish what you startmożesz również użyć // TODO:do zaznaczenia kodu do wypełnienia, który pojawi się w menu rozwijanym.
iwasrobbed
56

Napisz testy jednostkowe. Możesz dużo przetestować Kakao rzeczy, które mogą być trudniejsze w innych ramach. Na przykład za pomocą kodu interfejsu użytkownika można ogólnie sprawdzić, czy rzeczy są połączone tak, jak powinny, i ufać, że będą działać, gdy zostaną użyte. Możesz też łatwo skonfigurować metody stanowe i wywołać delegowane metody, aby je przetestować.

Nie masz też widoczności metody publicznej vs. chronionej vs. prywatnej, która przeszkadza w pisaniu testów dla twoich elementów wewnętrznych.

Chris Hanson
źródło
Jakie ramy testowe polecasz?
melfar
13
Xcode obejmuje OCUnit, platformę testowania jednostek Objective-C oraz obsługę uruchamiania pakietów testów jednostkowych w ramach procesu kompilacji.
Chris Hanson
55

Złota reguła: Jeśli ty, allocto ty release!

AKTUALIZACJA: Chyba że używasz ARC

logancautrell
źródło
26
Także, jeśli copy, mutableCopy, newlub retain.
Sven
54

Nie pisz Objective-C tak, jakby to był Java / C # / C ++ / etc.

Kiedyś widziałem, jak zespół przyzwyczajony do pisania aplikacji internetowych Java EE próbuje napisać aplikację komputerową Cocoa. Jakby była to aplikacja internetowa Java EE. Dużo latało w pobliżu AbstractFooFactory i FooFactory oraz IFoo i Foo, kiedy wszystko, czego naprawdę potrzebowali, to klasa Foo i ewentualnie protokół Fooable.

Częścią upewnienia się, że tego nie zrobisz, jest prawdziwe zrozumienie różnic w języku. Na przykład nie potrzebujesz abstrakcyjnych fabryk i klas fabrycznych powyżej, ponieważ metody klasy Objective-C są wysyłane tak dynamicznie jak metody instancji i mogą być zastępowane w podklasach.

Chris Hanson
źródło
10
Interesuję się jako programista Java, który napisał fabrykę abstrakcyjną w Objective-C. Czy mógłbyś wyjaśnić nieco więcej, jak to działa - być może na przykład?
teabot
2
Czy nadal uważasz, że nie potrzebujemy abstrakcyjnych klas fabrycznych po tak długim czasie, odkąd opublikowałeś tę odpowiedź?
kirk.burleson
50

Pamiętaj, aby dodać do zakładek Magię debugowania strony . To powinien być twój pierwszy przystanek, gdy uderzysz głową w ścianę, próbując znaleźć źródło błędu kakao.

Na przykład powie Ci, jak znaleźć metodę, w której najpierw przydzielono pamięć, która później powoduje awarie (na przykład podczas zamykania aplikacji).

mj1531
źródło
1
Dostępna jest teraz wersja Debugowania Magic przeznaczona dla systemu iOS .
Jeethu
38

Staraj się unikać tego, co postanowiłem nazywać Newbiecategoryaholism. Kiedy nowicjusze w Objective-C odkrywają kategorie, często szaleją, dodając użyteczne małe kategorie do każdej istniejącej klasy ( „Co? Mogę dodać metodę konwersji liczby na cyfry rzymskie na NSNumber rock!”. ).

Nie rób tego

Twój kod będzie bardziej przenośny i łatwiejszy do zrozumienia dzięki dziesiątkom metod kategorii pokrytych dwoma tuzinami klas podstawowych.

Przez większość czasu, kiedy naprawdę uważasz, że potrzebujesz metody kategorii, aby usprawnić trochę kodu, przekonasz się, że nigdy więcej nie użyjesz tej metody.

Istnieją również inne niebezpieczeństwa, chyba że używasz metod nazw kategorii (a kto oprócz całkowicie szalonego ddribina?) Istnieje szansa, że ​​Apple, wtyczka lub coś innego działającego w twojej przestrzeni adresowej również zdefiniuje tę samą kategorię metoda o tej samej nazwie z nieco innym efektem ubocznym ....

OK. Teraz, gdy zostałeś ostrzeżony, zignoruj ​​„nie rób tego”. Ale ćwicz ekstremalną powściągliwość.

schwa
źródło
Podoba mi się twoja odpowiedź, moja rada nie użyłaby kategorii do przechowywania kodu narzędzia, chyba że masz zamiar zreplikować jakiś kod w więcej niż jednym miejscu, a kod wyraźnie należy do klasy, którą zamierzasz kategoryzować ...
Kendall Helmstetter Gelner,
Chciałbym tylko zgłosić się i wyrazić poparcie dla metod kategorii przestrzeni nazw. To po prostu wydaje się słuszne.
Michael Buckley
+1, jeśli tylko dla cyfr rzymskich. Zrobiłbym to całkowicie!
Brian Postow
14
Kontrapunkt: przez ostatnie półtora roku przestrzegałem dokładnie przeciwnej zasady: „Jeśli można to zaimplementować w kategorii, zrób to”. W rezultacie mój kod jest znacznie bardziej zwięzły, bardziej wyrazisty i łatwiejszy do odczytania niż pełny przykładowy kod, który zapewnia Apple. Straciłem w sumie około 10 minut do jednego konfliktu przestrzeni nazw i prawdopodobnie zyskałem osobo-miesiące dzięki wydajnościom, które stworzyłem dla siebie. Do każdego z nich, ale przyjąłem tę politykę, znając ryzyko i jestem bardzo zadowolony, że to zrobiłem.
cduhn
7
Nie zgadzam się Jeśli będzie to funkcja i dotyczy obiektu Foundation, a możesz wymyślić dobre imię, umieść je w kategorii. Twój kod będzie bardziej czytelny. Myślę, że naprawdę istotnym punktem jest tutaj: rób wszystko z umiarem.
mxcl
37

Opieraj się podklasowaniu świata. W kakao wiele się dzieje poprzez delegowanie i używanie podstawowego środowiska wykonawczego, które w innych ramach odbywa się poprzez podklasowanie.

Na przykład w Javie *Listenerczęsto używasz instancji anonimowych podklas, aw .NET często używasz swoich EventArgspodklas. W Cocoa również tego nie robisz - zamiast tego używana jest akcja target.

Chris Hanson
źródło
6
W przeciwnym razie znany jako „Skład nad dziedziczeniem”.
Andrew Ebling,
37

Sortuj ciągi, jak chce użytkownik

Podczas sortowania ciągów, które mają być prezentowane użytkownikowi, nie należy używać prostej compare:metody. Zamiast tego należy zawsze używać zlokalizowanych metod porównywania, takich jak localizedCompare:lub localizedCaseInsensitiveCompare:.

Aby uzyskać więcej informacji, zobacz Wyszukiwanie, porównywanie i sortowanie ciągów .

mmalc
źródło
31

Zadeklarowane właściwości

Zazwyczaj należy używać funkcji deklarowanych właściwości Objective-C 2.0 dla wszystkich swoich właściwości. Jeśli nie są publiczne, dodaj je w rozszerzeniu klasy. Korzystanie z zadeklarowanych właściwości natychmiast poprawia semantykę zarządzania pamięcią i ułatwia sprawdzenie metody dealloc - jeśli zgrupujesz deklaracje właściwości razem, możesz szybko je przeskanować i porównać z implementacją metody dealloc.

Powinieneś dobrze przemyśleć, zanim nie oznaczysz właściwości jako „nieatomowe”. Jak zauważa Przewodnik po języku programowania Objective C , właściwości są domyślnie atomowe i powodują znaczne obciążenie. Co więcej, po prostu przekształcenie wszystkich właściwości w atomowe nie powoduje, że aplikacja jest bezpieczna dla wątków. Zauważ też oczywiście, że jeśli nie określisz „nonatomic” i nie zaimplementujesz własnych metod akcesorów (zamiast ich syntezy), musisz je zaimplementować w sposób atomowy.

mmalc
źródło
26

Pomyśl o zerowych wartościach

Tak jak tym pytaniu , komunikaty, które nilsą ważne w Celu C. Chociaż jest to często zaletą - prowadzi do czystszego i bardziej naturalnego kodu - funkcja ta może czasami prowadzić do osobliwych i trudnych do wyśledzenia błędów, jeśli otrzymasz nilwartość, gdy się jej nie spodziewałeś.

mmalc
źródło
Mam to: #define SXRelease(o); o = nili to samo dla CFReleasei free. Upraszcza to wszystko.
26

Użyj NSAssert i przyjaciół. Cały czas używam zera jako ważnego obiektu ... zwłaszcza wysyłanie wiadomości do zera jest całkowicie poprawne w Obj-C. Jeśli jednak naprawdę chcę się upewnić o stanie zmiennej, używam NSAssert i NSParameterAssert, co pomaga w łatwym śledzeniu problemów.

NikWest
źródło
23

Prosty, ale często zapomniany. Zgodnie ze specyfikacją:

Zasadniczo metody w różnych klasach, które mają ten sam selektor (tę samą nazwę), muszą również mieć takie same typy zwracanych argumentów i argumentów. To ograniczenie jest nakładane przez kompilator, aby umożliwić dynamiczne wiązanie.

w takim przypadku wszystkie selektory o tych samych nazwach, nawet jeśli w różnych klasach , będą uważane za mające identyczne typy return / argument. Oto prosty przykład.

@interface FooInt:NSObject{}
-(int) print;
@end

@implementation FooInt
-(int) print{
    return 5;
}
@end

@interface FooFloat:NSObject{}
-(float) print;
@end

@implementation FooFloat
-(float) print{
    return 3.3;
}
@end

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
    id f1=[[FooFloat alloc]init];
    //prints 0, runtime considers [f1 print] to return int, as f1's type is "id" and FooInt precedes FooBar
    NSLog(@"%f",[f1 print]);

    FooFloat* f2=[[FooFloat alloc]init];
    //prints 3.3 expectedly as the static type is FooFloat
    NSLog(@"%f",[f2 print]);

    [f1 release];
    [f2 release]
    [pool drain];

    return 0;
}   
Comptrol
źródło
łatwo zapomnieć. Ważne jednak
Brock Woolf
3
Jest to jedynie problem przy powstrzymywaniu się od pisania statycznego. Jeśli kompilator zna typ, typy argumentów i zwracane mogą się różnić bez problemów. Osobiście nie uważam tego za często problem. Apple ma również wiele metod o tej samej nazwie, ale różniących się typami zwrotów. Na koniec dostępna jest flaga kompilatora, która ostrzega w niejednoznacznych przypadkach.
Nikolai Ruhe
Jeśli będziemy postępować zgodnie z wytycznymi Apple dotyczącymi konwencji nazewnictwa, taka sytuacja się nie wydarzy :)
Eonil
22

Jeśli używasz systemu Leopard (Mac OS X 10.5) lub nowszego, możesz użyć aplikacji Instruments do wyszukiwania i śledzenia wycieków pamięci. Po zbudowaniu programu w Xcode wybierz Uruchom> Uruchom z Performance Tool> Wycieki.

Nawet jeśli Twoja aplikacja nie wykazuje żadnych wycieków, być może trzymasz obiekty zbyt długo. W Instrumentach możesz do tego użyć instrumentu ObjectAlloc. Wybierz instrument ObjectAlloc w dokumencie Instruments i wyświetl szczegóły instrumentu (jeśli jeszcze go nie pokazuje), wybierając Widok> Szczegół (obok niego powinien znajdować się znacznik wyboru). W obszarze „Długość życia alokacji” w szczegółach ObjectAlloc upewnij się, że wybrałeś przycisk opcji obok „Utworzono i wciąż żyję”.

Teraz, gdy przestaniesz rejestrować aplikację, wybranie narzędzia ObjectAlloc pokaże, ile odwołań do każdego wciąż żyjącego obiektu w aplikacji w kolumnie „# Net”. Upewnij się, że patrzysz nie tylko na własne klasy, ale także na klasy obiektów najwyższego poziomu w plikach NIB. Na przykład, jeśli nie masz okien na ekranie i widzisz odniesienia do wciąż działającego programu NSWindow, być może nie opublikowałeś go w kodzie.

mj1531
źródło
21

Posprzątaj w dealloc.

To jedna z najłatwiejszych rzeczy do zapomnienia - szczególnie. podczas kodowania z prędkością 150 mil na godzinę. Zawsze, zawsze, zawsze usuwaj atrybuty / zmienne składowe w dealloc.

Lubię używać atrybutów Objc 2 - z nową notacją kropkową - dzięki czemu czyszczenie jest bezbolesne. Często tak proste jak:

- (void)dealloc
{
    self.someAttribute = NULL;
    [super dealloc];
}

Spowoduje to załatwienie wydania i ustawienie atrybutu na NULL (co uważam za programowanie defensywne - w przypadku, gdy inna metoda w dalszej części dealloc ponownie uzyskuje dostęp do zmiennej członka - rzadko, ale może zdarzyć).

Po włączeniu GC w wersji 10.5 nie jest to już tak bardzo potrzebne - ale może być konieczne wyczyszczenie innych zasobów, które utworzysz, możesz to zrobić w metodzie finalizacji.

schwa
źródło
12
W ogóle należy nie używać akcesorów metod w dealloc (lub w init).
mmalc
1
Poza względami wydajnościowymi (akcesoria są nieco wolniejsze niż bezpośredni dostęp), dlaczego nie powinienem używać akcesoriów w dealloc lub init?
schwa
1
(a) Przyczyny wydajności są same w sobie wystarczającym powodem (szczególnie jeśli twoje akcesoria są atomowe). (b) Powinieneś unikać wszelkich skutków ubocznych, które mogą mieć akcesoria. To ostatnie jest szczególnie problematyczne, jeśli twoja klasa może zostać podklasowana.
mmalc
3
Zauważę, że jeśli korzystasz z nowoczesnego środowiska uruchomieniowego z syntezowanymi ivarami, musisz użyć akcesoriów w dealloc. Wiele współczesnych kodów wykonawczych to GC, ale nie wszystkie.
Louis Gerbarg,
1
Bardziej rozszerzony pogląd na to, czy należy używać metod / właściwości akcesoriów -initi -deallocmetod, można znaleźć tutaj: mikeash.com/?page=pyblog/...
Johan Kool
17

Wszystkie te komentarze są świetne, ale jestem naprawdę zaskoczony, że nikt nie wspomniał o Przewodniku stylu Objective-C Google, który został opublikowany jakiś czas temu. Myślę, że wykonali bardzo dokładną robotę.

slf
źródło
7
Hmm, pierwszy przykład jest już pełen bzdur. Nigdy nie dokumentuj idiomów językowych. Gdybym znalazł takie komentarze w pliku nagłówkowym, nie zawracałbym sobie głowy czytaniem dalej.
Stephan Eggermont
5
Och, moje oczy !!!!! Nie mogę uwierzyć w to, co widziałem.
Eonil
13

Nie zapominaj, że NSWindowController i NSViewController udostępnią obiekty najwyższego poziomu zarządzanych plików NIB.

Jeśli ręcznie załadujesz plik NIB, jesteś odpowiedzialny za zwolnienie obiektów najwyższego poziomu tego NIB, gdy już z nimi skończysz.

mj1531
źródło
12

Jeden dość oczywisty dla początkującego: użyj funkcji automatycznego wcięcia Xcode dla swojego kodu. Nawet jeśli kopiujesz / wklejasz z innego źródła, po wklejeniu kodu możesz zaznaczyć cały blok kodu, kliknąć go prawym przyciskiem myszy, a następnie wybrać opcję ponownego wcięcia wszystkiego w tym bloku.

Xcode parsuje tę sekcję i wcina ją na podstawie nawiasów, pętli itp. Jest to o wiele bardziej wydajne niż naciśnięcie klawisza spacji lub klawisza tabulacji dla każdej linii.

iWasRobbed
źródło
Możesz nawet ustawić Tab na wcięcie, a następnie zrobić Cmd-A i Tab.
Plumenator
10

Wiem, że przeoczyłem to po raz pierwszy w programowaniu Cocoa.

Upewnij się, że rozumiesz obowiązki zarządzania pamięcią dotyczące plików NIB. Odpowiadasz za zwolnienie obiektów najwyższego poziomu w każdym ładowanym pliku NIB. Przeczytaj dokumentację Apple na ten temat.

mj1531
źródło
6
To nie jest prawda. To, czy jesteś odpowiedzialny za uwalnianie obiektów najwyższego poziomu, zależy od tego, z której klasy dziedziczysz i jakiej platformy używasz. Zobacz między innymi developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/...
mmalc
10

Włącz wszystkie ostrzeżenia GCC, a następnie wyłącz te, które są regularnie powodowane przez nagłówki Apple w celu zmniejszenia hałasu.

Często też uruchamiaj analizę statyczną Clanga; możesz włączyć to dla wszystkich kompilacji za pomocą ustawienia kompilacji „Uruchom statyczny analizator”.

Napisz testy jednostkowe i uruchom je przy każdej kompilacji.

oefe
źródło
I jeśli możesz, włącz „Traktuj ostrzeżenia jako błędy”. Nie zezwalaj na istnienie żadnego ostrzeżenia.
Peter Hosey
2
Poręczny skrypt do skonfigurowania projektu z zalecanymi ostrzeżeniami jest dostępny tutaj: rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scpt
Johan Kool
10

Zmienne i właściwości

1 / Utrzymywanie nagłówków w czystości, ukrywanie implementacji
Nie dołączaj zmiennych instancji do nagłówka. Zmienne prywatne wprowadzone do kontynuacji klasy jako właściwości. Zmienne publiczne deklarują jako nagłówki właściwości publiczne. Jeśli powinien być tylko odczytany, zadeklaruj go jako tylko do odczytu i zastąp go jako readwrite w klasie kontynuacji. Zasadniczo nie używam zmiennych, tylko właściwości.

2 / Nadaj swoim właściwościom inną niż domyślna nazwę zmiennej, przykład:


@synthesize property = property_;

Powód 1: Wyłapiecie błędy spowodowane zapomnieniem „ja”. podczas przypisywania właściwości Powód 2: Z moich eksperymentów, Leak Analyzer in Instruments ma problemy z wykryciem przeciekającej właściwości o domyślnej nazwie.

3 / Nigdy nie używaj zatrzymania lub zwolnienia bezpośrednio na nieruchomościach (lub tylko w wyjątkowych sytuacjach). W swoim dealloc po prostu przypisz im zero. Właściwości zachowania służą do samodzielnego zarządzania zachowaniem / zwolnieniem. Nigdy nie wiadomo, czy osoba ustawiająca nie na przykład dodaje lub usuwa obserwatorów. Powinieneś używać zmiennej bezpośrednio tylko w jej seterze i getterze.

Wyświetlenia

1 / Umieść każdą definicję widoku w xib, jeśli możesz (wyjątkiem są zwykle dynamiczne ustawienia treści i warstw). Oszczędza czas (jest łatwiejszy niż pisanie kodu), łatwo go zmienić i utrzymuje kod w czystości.

2 / Nie próbuj optymalizować widoków, zmniejszając liczbę wyświetleń. Nie twórz UIImageView w swoim kodzie zamiast xib tylko dlatego, że chcesz do niego dodać widoki podrzędne. Zamiast tego użyj UIImageView jako tła. Struktura widoków może bez problemu obsługiwać setki widoków.

3 / IBOutlety nie zawsze muszą być utrzymywane (lub silne). Zauważ, że większość twoich IBOutletów jest częścią twojej hierarchii widoków, a zatem niejawnie zachowana.

4 / Zwolnij wszystkie IBOutlety w viewDidUnload

5 / Wywołanie viewDidUnload z metody dealloc. Nie jest to domyślnie nazywane.

Pamięć

1 / Automatyczne uwalnianie obiektów podczas ich tworzenia. Wiele błędów jest powodowanych przez przeniesienie wywołania release do jednej gałęzi if-else lub po instrukcji return. Zwolnienie zamiast automatycznego uwalniania powinno być używane tylko w wyjątkowych sytuacjach - np. Gdy czekasz na runloop i nie chcesz, aby twój obiekt był zbyt wcześnie automatycznie uwalniany.

2 / Nawet jeśli używasz automatycznego liczenia referencji, musisz doskonale zrozumieć, w jaki sposób działają metody zatrzymania-wydania. Ręczne korzystanie z retain-release nie jest bardziej skomplikowane niż ARC, w obu przypadkach musisz zająć się wyciekami i cyklami retain. Zastanów się nad ręcznym zachowaniem wydania w dużych projektach lub skomplikowanych hierarchiach obiektów.

Komentarze

1 / Ustaw kod w automatycznej dokumentacji. Każda nazwa zmiennej i nazwa metody powinny informować o tym, co robi. Jeśli kod jest napisany poprawnie (potrzebujesz w tym dużo praktyki), nie będziesz potrzebować żadnych komentarzy do kodu (nie to samo co komentarze do dokumentacji). Algorytmy mogą być skomplikowane, ale kod powinien być zawsze prosty.

2 / Czasami potrzebujesz komentarza. Zwykle w celu opisania nieoczywistego zachowania kodu lub włamania. Jeśli uważasz, że musisz napisać komentarz, najpierw spróbuj przepisać kod, aby był prostszy i nie wymagał komentarzy.

Wcięcie

1 / Nie zwiększaj nadmiernie wcięcia. Większość kodu metody powinna być wcięta na poziomie metody. Zagnieżdżone bloki (jeśli itp.) Zmniejszają czytelność. Jeśli masz trzy zagnieżdżone bloki, powinieneś spróbować umieścić bloki wewnętrzne w osobnej metodzie. Nigdy nie należy używać czterech lub więcej zagnieżdżonych bloków. Jeśli większość kodu metody znajduje się w if, zignoruj ​​warunek if, przykład:


if (self) {
   //... long initialization code ...
}

return self;

if (!self) {
   return nil;
}

//... long initialization code ...

return self;

Zrozum kod C, głównie struktury C.

Zauważ, że Obj-C jest tylko lekką warstwą OOP w języku C. Powinieneś zrozumieć, jak działają podstawowe struktury kodu w C (wyliczenia, struktury, tablice, wskaźniki itp.). Przykład:


view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);

jest taki sam jak:


CGRect frame = view.frame;
frame.size.height += 20;
view.frame = frame;

I wiele więcej

Zarządzaj własnym dokumentem standardów kodowania i często go aktualizuj. Spróbuj uczyć się na swoich błędach. Dowiedz się, dlaczego błąd został utworzony i staraj się go unikać, stosując standardy kodowania.

Nasze standardy kodowania obejmują obecnie około 20 stron, mieszankę standardów kodowania Java, standardów Google Obj-C / C ++ i własnych dodatków. Udokumentuj swój kod, użyj standardowych standardowych wcięć, białych spacji i pustych linii we właściwych miejscach itp.

Sulthan
źródło
9

Bądź bardziej funkcjonalny .

Objective-C jest językiem zorientowanym obiektowo, ale funkcjonuje w stylu Cocoa Framework i jest zaprojektowany w wielu przypadkach.

  1. Istnieje rozdzielność zmienności. Użyj niezmiennych klas jako podstawowego, a zmiennego obiektu jako wtórnego. Na przykład używaj przede wszystkim NSArray i NSMutableArray tylko wtedy, gdy potrzebujesz.

  2. Są czyste funkcje. Nie tak wielu, kup wiele frameworkowych interfejsów API zaprojektowanych jak czysta funkcja. Spójrz na funkcje takie jak CGRectMake()lub CGAffineTransformMake(). Oczywiście wskaźnik wygląda bardziej wydajnie. Jednak pośredni argument ze wskaźnikami nie może zaoferować efektów ubocznych. Projektuj struktury w jak największym stopniu. Oddziel nawet obiekty stanu. Użyj -copyzamiast, -retaingdy przekazujesz wartość do innego obiektu. Ponieważ stan współużytkowany może cicho wpływać na mutację na wartość w innym obiekcie. Więc nie może być bez skutków ubocznych. Jeśli masz wartość z zewnętrznego obiektu, skopiuj ją. Dlatego ważne jest również zaprojektowanie stanu współdzielonego tak minimalnie, jak to możliwe.

Nie obawiaj się jednak także korzystania z nieczystych funkcji.

  1. Jest leniwa ocena. Zobacz coś jak -[UIViewController view]własność. Widok nie zostanie utworzony podczas tworzenia obiektu. Zostanie utworzony viewprzy pierwszym czytaniu właściwości osoby dzwoniącej . UIImagenie zostanie załadowany, dopóki nie zostanie faktycznie narysowany. Istnieje wiele implementacji takich jak ten projekt. Tego rodzaju projekty są bardzo pomocne w zarządzaniu zasobami, ale jeśli nie znasz koncepcji leniwej oceny, nie jest łatwo zrozumieć ich zachowanie.

  2. Jest zamknięcie. Używaj C-bloków jak najwięcej. To znacznie uprości twoje życie. Ale przed użyciem przeczytaj jeszcze raz o zarządzaniu pamięcią bloków.

  3. Istnieje półautomatyczny GC. NSAutoreleasePool. Użyj -autoreleasepierwotnego. Użyj ręcznego -retain/-releasepomocniczego, gdy naprawdę potrzebujesz. (np .: optymalizacja pamięci, jawne usuwanie zasobów)

Eonil
źródło
2
Co do 3) Zaproponuję odwrotne podejście: w miarę możliwości używaj ręcznego zatrzymywania / zwalniania! Kto wie, jak ten kod będzie używany - a jeśli zostanie użyty w ciasnej pętli, może niepotrzebnie zwiększyć zużycie pamięci.
Eiko
@Eiko To tylko przedwczesna optymalizacja , nie może być ogólną wskazówką.
Eonil
1
Myślę, że to bardziej kwestia projektowania, szczególnie podczas pracy nad klasami modeli. Uważam, że powiększanie pamięci jest efektem ubocznym i nie chcę tego często pojawiać. Co gorsza, inny programista, który korzysta z mojego kodu, nie ma szans, jak tylko zawija drogie wywołania w pulach autorelease (jeśli to w ogóle możliwe - moje obiekty mogą być wysyłane do innego kodu biblioteki). Te problemy są trudne do zdiagnozowania później, ale przede wszystkim tanie, aby ich uniknąć. Jeśli skopiujesz / automatycznie wydasz przekazane obiekty, możesz zostać zgubiony, jeśli są znacznie większe niż się spodziewałeś. Jednak jestem bardziej zrelaksowany z kodem GUI.
Eiko
@Eiko Zgadzam się, autoreleaseże pamięć będzie przechowywana dłużej, a instrukcja retain/releasemoże zmniejszyć zużycie pamięci w przypadku. Jednak powinna to być wskazówka dotycząca optymalizacji przypadków specjalnych (nawet jeśli zawsze się czujesz!), Nie może być powodem do generalizowania przedwczesnej optymalizacji jako praktyki . W rzeczywistości twoja sugestia nie jest przeciwna do mnie. Wspomniałem o tym, jak naprawdę potrzebuję :)
Eonil