Самоанализ

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

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

Оценка отношений наследования

Как только Вы знаете класс, которому принадлежит объект, Вы, вероятно, знаете вполне немного об объекте. Вы могли бы знать, каковы его возможности, что приписывает его, представляет, и на какие виды сообщений это может ответить. Даже если после самоанализа Вы незнакомы с классом, которому принадлежит объект, Вы теперь знаете достаточно, чтобы не отправить ему определенные сообщения.

NSObject протокол объявляет несколько методов для определения позиции объекта в иерархии классов. Эти методы работают при различных гранулярностях. class и superclass методы экземпляра, например, возвращаются Class объекты, представляющие класс и суперкласс, соответственно, получателя. Эти методы требуют, чтобы Вы сравнили тот Class объект с другим. Перечисление 4-1 дает простое (можно было бы сказать тривиальный), пример их использования.

Перечисление 4-1  Используя class и superclass методы

// ...
while ( id anObject = [objectEnumerator nextObject] ) {
    if ( [self class] == [anObject superclass] ) {
        // do something appropriate...
    }
}

Более обычно, для проверки присоединения класса объекта Вы отправили бы ему a isKindOfClass: или isMemberOfClass: сообщение. Прежний метод возвращается, является ли получатель экземпляром данного класса или экземпляром какого-либо класса, наследовавшегося от того класса. A isMemberOfClass: если получатель является экземпляром указанного класса, сообщение, с другой стороны, говорит Вам. isKindOfClass: метод обычно более полезен, потому что от него можно знать сразу полный спектр сообщений, которые можно отправить в объект. Рассмотрите фрагмент кода в Перечислении 4-2.

Перечисление 4-2  Используя isKindOfClass:

if ([item isKindOfClass:[NSData class]]) {
    const unsigned char *bytes = [item bytes];
    unsigned int length = [item length];
    // ...
}

Путем узнавания, что объектный элемент наследовался от NSData класс, этот код знает, что это может отправить его NSDatabytes и length сообщения. Различие между isKindOfClass: и isMemberOfClass: становится очевидным, если Вы предполагаете, что элемент является экземпляром NSMutableData. Если Вы используете isMemberOfClass: вместо isKindOfClass:, код в блоке conditionalized никогда не выполняется, потому что элемент не является экземпляром NSData а скорее NSMutableData, подкласс NSData.

Реализация метода и соответствие протокола

Два из более мощных методов самоанализа NSObject respondsToSelector: и conformsToProtocol:. Эти методы говорят Вам, соответственно, реализует ли объект определенный метод и соответствует ли объект указанному формальному протоколу (т.е. принимает протокол, при необходимости, и реализует все методы протокола).

Вы используете эти методы в аналогичной ситуации в Вашем коде. Они позволяют Вам обнаружить, может ли некоторый потенциально анонимный объект соответственно реагировать на определенное сообщение или набор сообщений, прежде чем Вы отправите ему любое из тех сообщений. Путем осуществления этой проверки прежде, чем отправить сообщение, можно избежать риска исключений на этапе выполнения, следующих из нераспознанных селекторов. Платформа AppKit реализует неофициальные протоколы — основание делегации — путем проверки, реализуют ли делегаты метод делегации (использование respondsToSelector:) до вызова того метода.

Перечисление 4-3 иллюстрирует, как Вы могли бы использовать respondsToSelector: метод в Вашем коде.

Перечисление 4-3  Используя respondsToSelector:

- (void)doCommandBySelector:(SEL)aSelector {
    if ([self respondsToSelector:aSelector]) {
        [self performSelector:aSelector withObject:nil];
    } else {
        [_client doCommandBySelector:aSelector];
    }
}

Перечисление 4-4 иллюстрирует, как Вы могли бы использовать conformsToProtocol: метод в Вашем коде.

Перечисление 4-4  Используя conformsToProtocol:

// ...
if (!([((id)testObject) conformsToProtocol:@protocol(NSMenuItem)])) {
    NSLog(@"Custom MenuItem, '%@', not loaded; it must conform to the
        'NSMenuItem' protocol.\n", [testObject class]);
    [testObject release];
    testObject = nil;
}

Объектное сравнение

Несмотря на то, что они не строго методы самоанализа, hash и isEqual: методы выполняют подобную роль. Они - необходимые инструменты во время выполнения для идентификации и сравнения объектов. Но вместо того, чтобы запросить время выполнения для получения информации об объекте, они полагаются на специфичную для класса логику сравнения.

hash и isEqual: методы, оба объявленные NSObject протокол, тесно связаны. hash метод должен быть реализован для возврата целого числа, которое может использоваться в качестве табличного адреса в структуре хэш-таблицы. Если два объекта равны (как определено isEqual: метод), у них должно быть то же значение хэш-функции. Если Ваш объект мог бы быть включен в наборы такой как NSSet объекты, необходимо определить hash и проверьте инвариант, что, если два объекта равны, они возвращают то же значение хэш-функции. Значение по умолчанию NSObject реализация isEqual: просто проверки на равенство указателя.

Используя isEqual: метод является прямым; это сравнивает получатель с объектом, предоставленным в качестве параметра. Объектное сравнение часто сообщает решениям во время выполнения о том, что должно быть сделано с объектом. Поскольку Перечисление 4-5 иллюстрирует, можно использовать isEqual: решить, выполнить ли действие, в этом случае для сохранения измененных пользовательских настроек.

Перечисление 4-5  Используя isEqual:

- (void)saveDefaults {
    NSDictionary *prefs = [self preferences];
    if (![origValues isEqual:prefs])
        [Preferences savePreferencesToDefaults:prefs];
}

При создании подкласса Вы, возможно, должны были бы переопределить isEqual: добавить дальнейшие проверки на точки равенства. Подкласс мог бы определить дополнительный атрибут, который должен быть тем же значением в двух экземплярах для них, чтобы считаться равным. Например, скажите создание подкласса NSObject вызванный MyWidget это содержит две переменные экземпляра, name и data. Оба из них должны быть тем же значением для двух экземпляров MyWidget считаться равным. Перечисление 4-6 иллюстрирует, как Вы могли бы реализовать isEqual: для MyWidget класс.

  Переопределение перечисления 4-6 isEqual:

- (BOOL)isEqual:(id)other {
    if (other == self)
        return YES;
    if (!other || ![other isKindOfClass:[self class]])
        return NO;
    return [self isEqualToWidget:other];
}
 
- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
    if (self == aWidget)
        return YES;
    if (![(id)[self name] isEqual:[aWidget name]])
        return NO;
    if (![[self data] isEqualToData:[aWidget data]])
        return NO;
    return YES;
}

Это isEqual: метод сначала проверяет на равенство указателя, затем равенство класса, и наконец вызывает объектный компаратор, имя которого указывает класс объекта, вовлеченного в сравнение. Этот тип компаратора, вызывающего проверку типа объекта, переданного в, является общим соглашением в Какао; isEqualToString: метод NSString класс и isEqualToTimeZone: метод NSTimeZone класс является всего лишь двумя примерами. Специфичный для класса компаратор —isEqualToWidget: в этом случае — выполняет проверки на равенство данных и имя.

Всего isEqualToВвести: методы платформ Какао, nil не допустимый параметр, и реализации этих методов могут повысить исключение после получения a nil. Однако для обратной совместимости, isEqual: методы платформ Какао действительно принимают nil, возврат NO.