2012-03-21 3 views
3

В течение прошлого года я впервые работал с другими людьми в некоторых проектах Objective-C.Когда переопределить объективные c геттеры

Иногда (и все чаще) Я вижу других людей, переопределяющих методы getter/accessor, И содержащий код реализации в этом методе! Для меня это сумасшедший город, так как в этом весь смысл иметь сеттера ... это также означает, что свойство, установленное в сеттере, будет просто переопределено в геттере, и поэтому бессмысленно.

Эти люди ведут себя плохо, или я тот, кто чего-то не хватает? Есть ли необходимость переопределять метод getter с синтезированным свойством?

Пример:

@synthesize width; 

- (CGFloat)width { 
    NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:@"USER_WIDTH"]; 

    if (userWidth != nil) 
    { 
    width = [NSNumber floatValue]; 
    }  

    //Shouldn't the above lines be done in the SETTER? I have SET the property! 
    //Why would I ever need to write anything BUT the below line??  
    return width; 
} 

- (void)setWidth:(CGFloat)newWidth { 
    //THIS is where you do the the setting! 
    width = newWidth; 
} 

ОБНОВЛЕНИЕ:

Ok ширина является плохим примером. Слишком много людей сталкиваются с семантикой «какая переменная» и «не включают в себя доступ к объектным объектам». Поэтому я обновил приведенный выше пример, чтобы игнорировать нерелевантную семантику и сосредоточиться на концепции. Концепция ... есть ли какой-нибудь пример, когда вы хотите переопределить GETTER (не setter, getter only. Я многократно переопределяю setter, этот вопрос касается getter)?

Возвращение другого свойства, такого как слой (как указано ниже), является подлинным примером. Но, в частности, когда-либо возникает необходимость в SET Недвижимость в GETTER? Это какая-то странность, которую я вижу, поэтому я обновил getter выше, чтобы вытащить значение из NSUserDefaults, чтобы помочь моей точке ...

+0

вы можете добавить, как ширина определяется в заголовке? – bryanmac

ответ

5

Первая проблема заключается в том, что вы не хотите использовать getWidth. Шаблон в objC - это имя и setName. Не используйте getName. Это испортит привязку и KVO.

Кроме того, если это просто установка/получение iVar, нет причин для переопределения. Если вы выполняете дополнительную обработку/проверку, тогда может быть Ok переопределить.

EDIT:

Вы должны также попытаться избежать установки данных и делать тяжелую обработку в поглотителе.Геттер должен инкапсулировать некоторое состояние и вернуть данные. Ожидается, что это очень легкий вес. Тяжелая обработка и/или модификации должны выполняться в методах или сеттерах. Например, люди устанавливают отладочные часы на геттеры и не ожидают тяжелой обработки и изменения состояния.

+0

Спасибо. Для записи я никогда не добавляю имена аксессуаров, это была только поздняя ночная семантика. Давайте не будем отвлекаться от рассматриваемой концепции ... и это значит, нужно ли когда-либо устанавливать свойство в методе GETTER? – PostCodeism

+1

Я понимаю ваш вопрос лучше. Я обновил ответ. – bryanmac

+4

Lazy-экземпляр - пример шаблона, в котором вы, возможно, захотите реализовать геттер самостоятельно. –

0

Существует множество причин для переопределения методов получения и установки , например, я переопределяю методы setter, когда я создаю пользовательские объекты UITableViewCell, чтобы я мог установить свойство, а затем в рамках этого метода setter он автоматически обновит метку или что-то внутри ячейки, вместо того, чтобы мне нужно вызвать функцию обновления после установки свойства.

Возможно, вы захотите переписать получатель, если хотите хранить информацию по-другому, чем получать ее, примером может быть объект телефонного номера, где вы можете сохранить его как 5551234567, и он будет автоматически восстановлен как 555-123-4567 или что-то типа того. Я редко переоцениваю методы getter, но я довольно часто переопределяю методы setter.

+0

Это особенно полезно, когда у вас есть пользовательские элементы пользовательского интерфейса, которые используют пользовательские объекты данных, поэтому я не просто создаю свойства компонентов пользовательского интерфейса, тем проще просто использовать объект данных и переопределить сеттер. –

+0

Переопределение сеттера здесь не подлежит. Переопределение GETTER для установки свойства - вопрос ... – PostCodeism

0

Это вполне приемлемая практика в объектно-ориентированном программировании. Однако нужно знать о побочных эффектах. Например, вы не должны делать что-то вроде доступа к сети в методе setter.

Однако в коде, указанном выше, поскольку они не делают ничего, отличного от того, что делают синтезированные методы, нет причин включать реализации. Они просто загромождают код.

+0

Ваш первый абзац заслуживает доверия. Вы можете быть на чем-то здесь, в качестве подлинного примера, где вы должны установить свойство в методе GETTER. Пожалуйста, не слишком отвлекайтесь на приведенный выше код, это всего лишь пример поздней ночи с моей головы. Я обновил пример кода, чтобы лучше объяснить рассматриваемую концепцию. – PostCodeism

12

Во-первых, соглашения об именовании какао будут называть геттер -width, а не -getWidth. «Получить» используется для заполнения передается в качестве аргументов:

- (void) getWidth:(CGFloat *)outWidth 
{ 
    if (outWidth) *outWidth = _width; 
} 

Тем не менее, вернуться к исходному вопросу:

В старые времена, до того @property и @synthesize, мы должны написать наши аксессоров вручную вы сделали это выше.

Есть и другие случаи, когда вы хотели бы вручную написать аксессуар.

Одним из распространенных является отсрочка инициализации до тех пор, пока не потребуется значение. Например, скажем, что есть изображение, которое требуется для генерации. Каждый раз, когда вы изменяете свойство, которое изменило бы изображение, вы не хотите сразу перерисовывать изображение. Вместо этого, вы можете отложить жеребьевку до тех пор, когда в следующий раз кто-нибудь спрашивает:

- (UIImage *) imageThatTakesAwhileToGenerate 
{ 
    if (!_imageThatTakesAwhileToGenerate) { 
     // set it here 
    } 

    return _imageThatTakesAwhileToGenerate; 
} 


- (void) setColorOfImage:(UIColor *)color 
{ 
    if (_color != color) { 
     [_color release]; 
     _color = [color retain]; 

     // Invalidate _imageThatTakesAwhileToGenerate, we will recreate it the next time that the accessor is called 
     [_imageThatTakesAwhileToGenerate release]; 
     _imageThatTakesAwhileToGenerate = nil; 
    } 
} 

Другое использование направить реализацию аксессора/мутатором к другому классу. Например, UIView вперед многие его свойства для поддержки CALayer:

// Not actual UIKit implementation, but similar: 
- (CGRect) bounds { return [[self layer] bounds]; } 
- (void) setBounds:(CGRect)bounds { [[self layer] setBounds:bounds]; } 
- (void) setHidden:(BOOL)hidden { [[self layer] setHidden:hidden]; } 
- (BOOL) isHidden { return [[self layer] isHidden]; } 
- (void) setClipsToBounds:(BOOL)clipsToBounds { [[self layer] setMasksToBounds:clipsToBounds]; } 
- (BOOL) clipsToBounds { return [[self layer] masksToBounds]; } 

Update для обновления Аскера:

В вашем обновлении, это выглядит как код, в котором идет речь, либо пытается сохраняться значение ширины с помощью NSUserDefaults или пытается разрешить пользователям указывать настраиваемое значение для переопределения всех возвращенных значений ширины. Если последний, ваш пример в порядке (хотя я бы ограничил эту практику, так как это могло вызвать путаницу для новичков).

Если первый, вы хотите загрузить значение из NSUserDefaults один раз и сохранить новое значение обратно в NSUserDefaults в установщике. Например:

static NSString * const sUserWidthKey = @"USER_WIDTH"; 

@implementation Foo { 
    CGFloat _width; 
    BOOL _isWidthIvarTheSameAsTruthValue; 
} 

@synthesize width = _width; 

- (CGFloat) width 
{ 
    if (!_isWidthIvarTheSameAsTruthValue) { 
     NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:sUserWidthKey]; 
     if (userWidth != nil) _width = [NSNumber doubleValue]; 
     _isWidthIvarTheSameAsTruthValue = YES; 
    } 

    return _width; 
} 

- (void) setWidth:(CGFloat)newWidth 
{ 
    if (_width != newWidth) { 
     _width = newWidth; 
     NSNumber *userWidthNumber = [NSNumber numberWithDouble:_width]; 
     [[NSUserDefaults standardUserDefaults] setObject:userWidthNumber forKey:sUserWidthKey]; 
     _isWidthIvarTheSameAsTruthValue = YES; 
    } 
} 

@end 

_width ivar используется как кэш. Истина хранится в NSUserDefaults.

Примечание: Я использую NSUserDefaults в этом примере, так как вы использовали его в своем. На практике я предпочитаю не смешивать NSUserDefault с моими аксессуарами;)

+0

Отличное объяснение. –

+0

+1 на более подробной информации – bryanmac

+0

Спасибо за ваш вклад. Пример слоя - это самый близкий ответ, который я искал, и, безусловно, хороший кандидат для переопределения получателя. Тем не менее, это технически не НАСТРОЙКА свойства в методе GETTER (который является рассматриваемой концепцией) ... есть ли когда-нибудь время, когда это выгодно или эта плохая практика? – PostCodeism

1

Как насчет случая, когда вы создаете свой объект недвижимости лениво? Я использую эту модель очень часто, также используется в шаблоне CoreData Xcode, в и т.д.

- (NSString *)string 
{ 
    if (!_string) { 
     // Create the string property lazily 
     // Create is using some other internal, etc values 
     _string = [NSString alloc] initWith...] 
    } 
    return _string; 
} 

также:

- (void)setString:(NSString *)string 
{ 
    if (![string isEqualToString:_string]) { 
     // Probably you want to make your property observable here too :) 

     [_setString release]; 
     _setString = [string retain]; 

     // Update other things that depend on _string for example redraw the view, etc 
     [self setNeedsDisplay]; 
    } 
} 
Смежные вопросы