Po co Ci ivar?
Zwykle widzę to pytanie zadawane w inny sposób, np. Czy każdy ivar musi być własnością? (i podoba mi się odpowiedź bbuma na to pytanie).
Używam właściwości prawie wyłącznie w moim kodzie. Co jakiś czas jednak współpracuję z wykonawcą, który od dawna pracuje na iOS i jest tradycyjnym programistą gier. Pisze kod, który deklaruje prawie żadnych właściwości i opiera się na ivars. Zakładam, że robi to dlatego, że 1.) przyzwyczaił się do tego, ponieważ właściwości nie istnieją zawsze do czasu osiągnięcia celu C 2.0 (październik ' 07) i 2.) dla minimalnego przyrostu wydajności nie przechodząc przez getter / setter.
Kiedy pisze kod, który nie przecieka, wolałbym, żeby używał właściwości niż ivars. Rozmawialiśmy o tym i on mniej więcej nie widzi powodu, aby używać właściwości, ponieważ nie używaliśmy KVO i ma doświadczenie w dbaniu o problemy z pamięcią.Moje pytanie jest więcej... Dlaczego w ogóle chcesz używać okresu ivar-doświadczenie czy nie. Czy naprawdę jest tak wielka różnica w wydajności, że używanie Ivara byłoby uzasadnione?
Również jako punkt wyjaśnienia, w razie potrzeby nadpisuję settery i gettery i używam ivar, który koreluje z tą właściwością wewnątrz gettera / settera. Jednak poza getter / setter lub init, zawsze używam składni self.myProperty
.
Edycja 1
Doceniam wszystkie dobre odpowiedzi. Chciałbym zwrócić się do osoby, która wydaje się niepoprawna, to że z Ivarem dostajesz enkapsulację tam, gdzie z właściwością nie masz. po prostu zdefiniuj właściwość w kontynuacji klasy. To ukryje własność przed obcymi. Można również zadeklarować właściwość readonly w interfejsie i przedefiniować ją jako readwrite w implementacji jak:// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;
I mieć w klasie kontynuację:
// readwrite within this file
@property (nonatomic, copy) NSString * name;
Mieć go całkowicie "prywatny" tylko zadeklarować go w klasie kontynuacja.
7 answers
Enkapsulacja
Jeśli ivar jest prywatny, Inne części programu nie mogą dostać się do niego tak łatwo. Z zadeklarowaną własnością, sprytni ludzie mogą uzyskać dostęp i mutować dość łatwo za pośrednictwem accessorów.
Wydajność
Tak, to może mieć znaczenie w niektórych przypadkach. Niektóre programy mają ograniczenia, w których nie mogą używać żadnych komunikatów objc w niektórych częściach programu (think realtime). W innych przypadkach możesz chcieć uzyskać do niego bezpośredni dostęp dla szybkości. W innych przypadkach jest to ponieważ komunikatory objc działają jak Zapora optymalizacyjna. Wreszcie, może zmniejszyć operacje zliczania referencji i zminimalizować szczytowe zużycie pamięci (jeśli zrobione poprawnie).Typy Nietrywialne
Przykład: jeśli masz typ C++, bezpośredni dostęp jest po prostu lepsze podejście czasami. Typ może nie być kopiowalny lub kopiowanie może nie być trywialne.
Wielowątkowość
Wiele z Twoich ivarów jest współzależnych. Musisz zapewnić integralność danych w kontekście wielowątkowym. Tak więc, ty może faworyzować bezpośredni dostęp do wielu członków w sekcjach krytycznych. Jeśli będziesz trzymać się accesorów dla danych współzależnych, Twoje blokady muszą być zazwyczaj reentrantowe i często będziesz dokonywał o wiele więcej zakupów(czasami znacznie więcej).Poprawność Programu
Ponieważ podklasy mogą zastąpić dowolną metodę, możesz w końcu zobaczyć różnicę semantyczną między pisaniem do interfejsu a odpowiednim zarządzaniem stanem. Bezpośredni dostęp do poprawności programu jest szczególnie powszechne w częściowo skonstruowanych stanach - w Twoich inicjalizatorach i w dealloc
, najlepiej jest użyć bezpośredniego dostępu. Można to również znaleźć w implementacjach accessora, konstruktora wygody, copy
, mutableCopy
, oraz wdrożenia archiwizacji/serializacji.
Jest to również częstsze, ponieważ przenosi się z Wszystko ma publiczny accesor readwrite do takiego, który dobrze ukrywa szczegóły/dane implementacji. Czasami trzeba poprawnie przejść efekty uboczne nadpisanie podklasy może wprowadzić w celu zrobienia właściwej rzeczy.
Rozmiar Binarny
Domyślne deklarowanie wszystkiego readwrite zwykle skutkuje wieloma metodami dostępu, których nigdy nie potrzebujesz, jeśli przez chwilę rozważysz wykonanie programu. Więc doda trochę tłuszczu do twojego programu i czasu ładowania.
Minimalizuje Złożoność
W niektórych przypadkach po prostu zupełnie niepotrzebne jest dodawanie + Typ+utrzymanie wszystkich dodatkowych rusztowań dla prostej zmiennej na przykład prywatny bool, który jest zapisywany w jednej metodzie, a odczytywany w innej.
Nie oznacza to wcale, że korzystanie z właściwości lub akcesoriów jest złe - każdy z nich ma ważne korzyści i ograniczenia. Podobnie jak wiele języków OO i podejść do projektowania, należy również faworyzować Accesory o odpowiedniej widoczności w ObjC. Będą chwile, kiedy będziesz musiał się zboczyć. Z tego powodu uważam, że często najlepiej jest ograniczyć bezpośredni dostęp do implementacji, która deklaruje ivar (np. declare it @private
).
Re Edytuj 1:
Większość z nas zapamiętała, jak dynamicznie wywoływać Ukryty akcesor (o ile znamy nazwę...). Tymczasem większość z nas ma Nie zapamiętane, jak prawidłowo uzyskać dostęp do ivarów, które nie są widoczne (poza KVC). Kontynuacja klasy pomaga , ale wprowadza luki.
To obejście jest oczywiste:
if ([obj respondsToSelector:(@selector(setName:)])
[(id)obj setName:@"Al Paca"];
Teraz spróbuj tylko z Ivarem i bez KVC.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-31 13:44:09
Dla mnie to zazwyczaj performance. Dostęp do ivar obiektu jest tak szybki, jak dostęp do elementu struktury w C za pomocą wskaźnika do pamięci zawierającej taką strukturę. W rzeczywistości Obiekty Objective - C są w zasadzie strukturami C umieszczonymi w dynamicznie przydzielanej pamięci. Zwykle jest to tak szybkie, jak Twój kod może uzyskać, nawet ręcznie zoptymalizowany kod złożenia nie może być szybszy niż to.
Dostęp do ivar poprzez getter / ustawienie wymaga wywołania metody Objective-C, która jest znacznie wolniejsza (w co najmniej 3-4 razy) niż "normalne" wywołanie funkcji C, a nawet normalne wywołanie funkcji C byłoby już wielokrotnie wolniejsze niż uzyskiwanie dostępu do elementu struct. W zależności od atrybutów właściwości, implementacja setter / getter generowana przez kompilator może obejmować kolejne wywołanie funkcji C do funkcji objc_getProperty
/objc_setProperty
, ponieważ będą one musiały retain
/copy
/autorelease
obiekty w razie potrzeby i dalej wykonywać spinlocking dla właściwości atomowych w razie potrzeby. To może łatwo stać się bardzo drogie i Nie mówię o byciu 50% wolniejszym.
Spróbujmy tego:
CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;
cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);
cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
[self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);
Wyjście:
1: 23.0 picoseconds/run
2: 98.4 picoseconds/run
To jest 4.28 razy wolniejsze i to był nieatomowy prymitywny int, prawie najlepszy przypadek ; większość innych przypadków jest jeszcze gorsza (spróbuj atomu NSString *
właściwość!). Jeśli więc można żyć z faktem, że każdy dostęp ivar jest 4-5 razy wolniejszy niż mógłby być, Korzystanie z właściwości jest w porządku( przynajmniej jeśli chodzi o wydajność), jednak jest wiele sytuacji, w których takie spadek wydajności jest całkowicie niedopuszczalny.
Aktualizacja 2015-10-20
Niektórzy twierdzą, że nie jest to prawdziwy problem, powyższy kod jest czysto syntetyczny i nigdy nie zauważysz tego w prawdziwej aplikacji. W takim razie spróbujmy prawdziwej próbki.
Poniższy kod definiuje Account
obiekty. Konto ma właściwości opisujące imię (NSString *
), Płeć (enum
) i wiek (unsigned
) jego właściciela, a także równowagę (int64_t
). Obiekt konta posiada metoda init
i metoda compare:
. Metoda compare:
jest zdefiniowana jako: kolejność żeńska przed męską, kolejność imion Alfabetycznie, kolejność młoda przed starą, kolejność równowagi od niskiej do wysokiej.
AccountA
i AccountB
. Jeśli spojrzysz na ich implementację, zauważysz, że są prawie całkowicie identyczne, z jednym wyjątkiem: metoda compare:
. AccountA
obiekty uzyskują dostęp do własnych właściwości metodą (getter), natomiast AccountB
obiekty uzyskują dostęp do swoich własne właściwości by ivar. To naprawdę jedyna różnica! Obaj uzyskują dostęp do właściwości innego obiektu, aby porównać go przez getter (dostęp do niego przez Ivara nie byłby bezpieczny! Co jeśli drugi obiekt jest podklasą i przesłonił getter?). Zauważ również, że dostęp do własnych właściwości jako ivars nie powoduje przerwania enkapsulacji (ivars nadal nie są publiczne).
Konfiguracja testu jest naprawdę prosta: Utwórz losowe konta 1 Mio, dodaj je do tablicy i posortuj / align = "left" / To wszystko. Oczywiście istnieją dwie tablice, jedna dla obiektów AccountA
i jedna dla obiektów AccountB
i obie tablice są wypełnione identycznymi kontami (to samo źródło danych). Mamy czas, jak długo trwa sortowanie tablic.
Oto wynik kilku biegów, które zrobiłem wczoraj:
runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076
Jak widzisz, sortowanie tablicy AccountB
obiektów jest zawsze znacznie szybsze niż sortowanie tablicy AccountA
obiektów.
Kto twierdzi, że różnice czasu trwania up do 1.32 sekund nie robi różnicy powinien lepiej nigdy nie robić programowania interfejsu użytkownika. Jeśli chcę zmienić kolejność sortowania dużej tabeli, na przykład, różnice czasowe, takie jak te, robią ogromną różnicę dla użytkownika (różnica między akceptowalnym i powolnym interfejsem użytkownika).
Również w tym przypadku przykładowy kod jest jedyną prawdziwą pracą wykonywaną tutaj, ale jak często Twój kod jest tylko małym biegiem skomplikowanego mechanizmu? A jeśli każdy sprzęt spowalnia cały proces w ten sposób, co to robi średnia dla prędkości całego zegara w końcu? Zwłaszcza, jeśli jeden krok roboczy zależy od wyniku innego, co oznacza, że wszystkie nieefektywności zostaną podsumowane. Większość nieefektywności nie jest problemem sama w sobie, to sama ich suma staje się problemem dla całego procesu. I takim problemem jest nic, co profiler łatwo pokaże, ponieważ profiler polega na znalezieniu krytycznych hot spotów, ale żadna z tych nieefektywności nie jest hot spotami sama w sobie. Czas procesora jest po prostu średnio rozłożony wśród nich jednak każdy z nich ma tylko tak niewielki ułamek, że wydaje się całkowitą stratą czasu na jego optymalizację. I to prawda, optymalizacja tylko jednego z nich nie pomoże absolutnie nic, optymalizacja wszystkich z nich może pomóc dramatycznie.
I nawet jeśli nie myślisz w kategoriach czasu procesora, ponieważ uważasz, że marnowanie czasu procesora jest całkowicie dopuszczalne, w końcu "to za darmo", to co z kosztami hostingu serwera spowodowanymi zużyciem energii? Co z czasem pracy baterii urządzeń mobilnych? Jeśli napisałbyś dwa razy tę samą aplikację mobilną (np. własną mobilną przeglądarkę internetową), raz wersję, w której wszystkie klasy mają dostęp do własnych właściwości tylko przez gettery i raz, w której wszystkie klasy mają dostęp do nich tylko przez ivars, używanie pierwszej stale na pewno wyczerpie baterię znacznie szybciej niż używanie drugiej, mimo że są one funkcjonalnym odpowiednikiem, a dla użytkownika druga byłaby nawet prawdopodobnie jeszcze szybsza.
Oto kod do pliku main.m
(Kod polega na włączeniu ARC i pamiętaj, aby użyć optymalizacji podczas kompilacji, aby zobaczyć pełny efekt):
#import <Foundation/Foundation.h>
typedef NS_ENUM(int, Gender) {
GenderMale,
GenderFemale
};
@interface AccountA : NSObject
@property (nonatomic) unsigned age;
@property (nonatomic) Gender gender;
@property (nonatomic) int64_t balance;
@property (nonatomic,nonnull,copy) NSString * name;
- (NSComparisonResult)compare:(nonnull AccountA *const)account;
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance;
@end
@interface AccountB : NSObject
@property (nonatomic) unsigned age;
@property (nonatomic) Gender gender;
@property (nonatomic) int64_t balance;
@property (nonatomic,nonnull,copy) NSString * name;
- (NSComparisonResult)compare:(nonnull AccountB *const)account;
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance;
@end
static
NSMutableArray * allAcocuntsA;
static
NSMutableArray * allAccountsB;
static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
assert(min <= max);
uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
rnd = (rnd << 32) | arc4random();
rnd = rnd % ((max + 1) - min); // Trim it to range
return (rnd + min); // Lift it up to min value
}
static
void createAccounts ( const NSUInteger ammount ) {
NSArray *const maleNames = @[
@"Noah", @"Liam", @"Mason", @"Jacob", @"William",
@"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
];
NSArray *const femaleNames = @[
@"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
@"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
];
const NSUInteger nameCount = maleNames.count;
assert(maleNames.count == femaleNames.count); // Better be safe than sorry
allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
allAccountsB = [NSMutableArray arrayWithCapacity:ammount];
for (uint64_t i = 0; i < ammount; i++) {
const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
const unsigned age = (unsigned)getRandom(18, 120);
const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;
NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
NSString *const name = nameArray[nameIndex];
AccountA *const accountA = [[AccountA alloc]
initWithName:name age:age gender:g balance:balance
];
AccountB *const accountB = [[AccountB alloc]
initWithName:name age:age gender:g balance:balance
];
[allAcocuntsA addObject:accountA];
[allAccountsB addObject:accountB];
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
@autoreleasepool {
NSUInteger ammount = 1000000; // 1 Million;
if (argc > 1) {
unsigned long long temp = 0;
if (1 == sscanf(argv[1], "%llu", &temp)) {
// NSUIntegerMax may just be UINT32_MAX!
ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
}
}
createAccounts(ammount);
}
// Sort A and take time
const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
@autoreleasepool {
[allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
}
const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;
// Sort B and take time
const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
@autoreleasepool {
[allAccountsB sortedArrayUsingSelector:@selector(compare:)];
}
const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;
NSLog(@"runTime 1: %f", runTime1);
NSLog(@"runTime 2: %f", runTime2);
}
return 0;
}
@implementation AccountA
- (NSComparisonResult)compare:(nonnull AccountA *const)account {
// Sort by gender first! Females prior to males.
if (self.gender != account.gender) {
if (self.gender == GenderFemale) return NSOrderedAscending;
return NSOrderedDescending;
}
// Otherwise sort by name
if (![self.name isEqualToString:account.name]) {
return [self.name compare:account.name];
}
// Otherwise sort by age, young to old
if (self.age != account.age) {
if (self.age < account.age) return NSOrderedAscending;
return NSOrderedDescending;
}
// Last ressort, sort by balance, low to high
if (self.balance != account.balance) {
if (self.balance < account.balance) return NSOrderedAscending;
return NSOrderedDescending;
}
// If we get here, the are really equal!
return NSOrderedSame;
}
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance
{
self = [super init];
assert(self); // We promissed to never return nil!
_age = age;
_gender = gender;
_balance = balance;
_name = [name copy];
return self;
}
@end
@implementation AccountB
- (NSComparisonResult)compare:(nonnull AccountA *const)account {
// Sort by gender first! Females prior to males.
if (_gender != account.gender) {
if (_gender == GenderFemale) return NSOrderedAscending;
return NSOrderedDescending;
}
// Otherwise sort by name
if (![_name isEqualToString:account.name]) {
return [_name compare:account.name];
}
// Otherwise sort by age, young to old
if (_age != account.age) {
if (_age < account.age) return NSOrderedAscending;
return NSOrderedDescending;
}
// Last ressort, sort by balance, low to high
if (_balance != account.balance) {
if (_balance < account.balance) return NSOrderedAscending;
return NSOrderedDescending;
}
// If we get here, the are really equal!
return NSOrderedSame;
}
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance
{
self = [super init];
assert(self); // We promissed to never return nil!
_age = age;
_gender = gender;
_balance = balance;
_name = [name copy];
return self;
}
@end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 08:35:05
Najważniejszym powodem jest koncepcja OOP ukrywania informacji : Jeśli ujawnisz wszystko za pomocą właściwości i w ten sposób sprawisz, że zewnętrzne obiekty będą podglądać wewnętrzne obiekty innego obiektu, to wykorzystasz te wewnętrzne i tym samym skomplikuj zmianę implementacji.
Przyrost "minimalnej wydajności" może szybko się podsumować, a następnie stać się problemem. Wiem z doświadczenia; pracuję nad aplikacją, która naprawdę przenosi iDevices do granic możliwości i dlatego musimy unikać niepotrzebne wywołania metod (oczywiście tylko tam, gdzie jest to możliwe). Aby pomóc w osiągnięciu tego celu, unikamy również składni kropki, ponieważ sprawia, że trudno jest zobaczyć liczbę wywołań metod na pierwszy rzut oka: na przykład, ile wywołań metod wywołuje wyrażenie self.image.size.width
? Dla kontrastu, można od razu powiedzieć za pomocą [[self image] size].width
.
Również, przy poprawnym nazewnictwie ivar, KVO jest możliwe bez właściwości (IIRC, nie jestem ekspertem KVO).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-31 21:11:39
Semantyka
- co
@property
może wyrazić, że ivars nie może:nonatomic
icopy
. - co ivars może wyrazić, że
@property
nie może:-
@protected
: publiczne na podklasach, prywatne Na Zewnątrz. -
@package
: publiczny na frameworkach na 64 bitach, prywatny Na Zewnątrz. Tak samo jak@public
na 32 bitach. Zobacz 64-bitową kontrolę dostępu klasy I instancji firmy Apple . - Kwalifikacje. Na przykład tablice silnych odniesień do obiektów:
id __strong *_objs
.
-
Wydajność
Krótka historia: Ivary są szybsze, ale nie ma to znaczenia dla większości zastosowań. nonatomic
properties nie używają zamków, ale direct ivar jest szybszy, ponieważ pomija wywołanie accessors. Aby uzyskać szczegółowe informacje, przeczytaj następujący E-mail od lists.apple.com.
Subject: Re: when do you use properties vs. ivars? From: John McCall <email@hidden> Date: Sun, 17 Mar 2013 15:10:46 -0700
Właściwości wpływają na wydajność na wiele sposobów:
Jak już wspomniano, wysyłanie wiadomości do load/store jest wolniejsze niż tylko robienie load/store inline .
Wysyłanie wiadomości do load/store to również trochę więcej kodu , który musi być przechowywany w i-cache: nawet jeśli getter / setter dodano zero dodatkowych instrukcji poza samym ładowaniem/przechowywaniem, byłoby solidne pół tuzina dodatkowych instrukcji w rozmówcy, aby skonfigurować wyślij wiadomość i obsłuż wynik.
Wysłanie wiadomości wymusza zapisanie tego selektora w metodzie cache , a pamięć ta generalnie trzyma się w D-cache. Zwiększa to czas uruchamiania, zwiększa pamięć statyczną korzystanie z aplikacji i sprawia, że przełączniki kontekstowe są bardziej bolesne. Ponieważ metoda cache jest specyficzna dla klasy dynamic dla obiektu, to problem wzrasta, im więcej używasz na nim KVO.
Wysłanie wiadomości wymusza przelanie wszystkich wartości w funkcji na stos (lub zachowanie w rejestrach callee-save, co oznacza rozlewanie na inny czas).
Wysłanie wiadomości może mieć dowolne skutki uboczne i dlatego
- zmusza kompilator do zresetowania wszystkich założeń dotyczących pamięci nielokalnej
Nie można go podnieść, zatopić, ponownie zamówić, połączyć lub wyeliminować.
W ARC, wynik wysłanej wiadomości będzie zawsze zachowywany, przez wywołującego lub wywołującego, nawet dla + 0 zwraca: nawet jeśli metoda nie zachowuje / autorelease swojego wyniku, wywołujący nie wie że I musi starać się podjąć działania, aby nie dopuścić do uzyskania wyniku autoreleased. Tego nigdy nie można wyeliminować, ponieważ wysyłane wiadomości są nie można analizować statycznie.
W ARC, ponieważ metoda settera zazwyczaj przyjmuje swój argument na + 0, nie ma możliwości "przeniesienia" zachowania tego obiektu (co, jak omówione powyżej, ARC zwykle ma) do ivar, więc wartość generalnie musi dostać retain / released twice .
Nic z tego nie oznacza, że zawsze są złe , oczywiście-są wiele dobrych powodów, aby używać właściwości. Pamiętaj tylko, że jak wiele innych funkcji językowych nie jest darmowych.
John.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-05 20:25:53
Kompatybilność wsteczna była dla mnie czynnikiem. Nie mogłem użyć żadnych funkcji Objective-C 2.0, ponieważ tworzyłem oprogramowanie i sterowniki drukarek, które musiały działać na Mac OS X 10.3 jako część wymagań. Wiem, że twoje pytanie wydawało się ukierunkowane na iOS, ale pomyślałem, że nadal podzielę się moimi powodami, dla których nie używam właściwości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-31 21:16:59
Properties vs. instance variables to kompromis, w końcu wybór sprowadza się do aplikacji.
Enkapsulacja / ukrywanie informacji jest to dobra rzecz (TM) z perspektywy projektowej, wąskie interfejsy i minimalne powiązania sprawiają, że oprogramowanie jest łatwe do utrzymania i zrozumiałe. W Obj - C trudno jest cokolwiek ukryć, ale zmienne instancji zadeklarowane w implementacji są tak blisko, jak się da.
Performance While " premature optymalizacja " to zła rzecz (TM), pisanie źle wykonującego kodu tylko dlatego, że możesz jest co najmniej tak samo złe. Trudno jest argumentować, że wywołanie metody jest droższe niż obciążenie lub sklep, a w intensywnym kodzie obliczeniowym koszt wkrótce się sumuje.
W języku statycznym z właściwościami, takimi jak C#, wywołania setterów/getterów mogą być często optymalizowane przez kompilator. Jednak Obj-C jest dynamiczny i usuwanie takich połączeń jest znacznie trudniejsze.
Abstrakcja argument przeciw zmienne instancyjne w Obj - C tradycyjnie były zarządzaniem pamięcią. Ponieważ zmienne instancji MRC wymagają wywołań do zachowania / wydania / autorelease, aby były rozłożone w całym kodzie, właściwości (syntetyzowane lub nie) utrzymują kod MRC w jednym miejscu - zasada abstrakcji, która jest dobrą rzeczą (TM). Jednak w przypadku GC lub ARC argument ten znika, więc abstrakcja dla zarządzania pamięcią nie jest już argumentem wobec zmiennych instancji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-31 21:19:55
Właściwości narażają Twoje zmienne na inne klasy. Jeśli potrzebujesz tylko zmiennej, która jest tylko względna do tworzonej klasy, użyj zmiennej instancji. Oto mały przykład: klasy XML do parsowania RSS i tym podobnych przechodzą przez kilka metod delegowania i tym podobne. Praktycznym jest posiadanie instancji NSMutableString do przechowywania wyniku każdego innego przejścia parse. Nie ma powodu, dla którego Klasa zewnętrzna musiałaby kiedykolwiek uzyskać dostęp lub manipulować tym łańcuchem. Więc, wystarczy zadeklarować go w nagłówku lub prywatnie i uzyskać do niego dostęp w całej klasie. Ustawienie właściwości it może być przydatne tylko w celu upewnienia się, że nie ma problemów z pamięcią, używając self.mutableString do wywołania getter/setters.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-31 21:02:00