2010-07-13 2 views
5

Я написал макрос в Objective-C для безопасного переноса. Вот как это выглядит так далеко:Objective-C Safe Casting Macro

#define SAFE_CAST(OBJECT, TYPE) ([OBJECT isKindOfClass:[TYPE class]] ? (TYPE *) OBJECT: nil) 

Это работает очень хорошо, но было бы неплохо, если бы был способ сохранить объект в переменной, так что не было дозвонились дважды. Например, используя макрос, как например:

NSString *str = SAFE_CAST([dictinary objectForKey:key], NSString); 

результаты в коде аналогично этому, когда макрос расширяется:

NSString *str = ([[dictinary objectForKey:key] isKindOfClass:[NSString class]] ? (NSString *) [dictinary objectForKey:key]: nil); 

Я бы предпочел, чтобы он работал больше, как это:

id obj = [dictionary objectForKey:key]; 
NSString *str = ([obj objectForKey:key] isKindOfClass[NSString class]] ? (NSString *) obj : nil); 

Спасибо.

+0

Я не вижу, что точка это. Вы сказали, что хотите использовать его для дезинфекции plists, но, конечно, если вы используете это, вам нужно проверить, нет ли возвращенного объекта? Почему бы просто не проверить, есть ли объект isKindOfClass: expectedClass? – JeremyP

ответ

8

Вы можете использовать расширение GCC под названием заявление statement expressions иметь

#define SAFE_CAST(OBJECT, TYPE) ({ id obj=OBJECT;[obj isKindOfClass:[TYPE class]] ? (TYPE *) obj: nil; }) 

То есть, я думаю, что это в общем плохой подход, чтобы иметь такую ​​ситуацию, когда вам нужно использовать SAFE_CAST много. Никогда не кладите в массив объекты разных классов; никогда не используйте повторно действие сообщения (IBAction)someAction:(id)sender для объектов пользовательского интерфейса разных классов. Тогда вам обычно не нужно использовать SAFE_CAST.

+1

Прямо сейчас я использую этот макрос при чтении информации из plist. Я не гарантирую, что содержимое plist действительно. Это похоже на любой другой актерский состав; его можно злоупотреблять, но он использует его. Если есть лучший способ сделать это, не могли бы вы рассказать? По вашему ответу выражения операторов выглядят интересными. Могут ли они использоваться с LLVM? – LandonSchropp

+0

Да, он работает с 'clang'. Что касается 'plist', я согласен с использованием' SAFE_CAST', если он предоставлен пользователем. Но если это не так, я думаю, что лучше использовать 'NSAssert' при разработке, так что программа сильно сработает, когда я готовлю недопустимый plist; для сборки релиза вашего приложения не требуется 'SAFE_CAST', потому что вы убедитесь, что' plist' действителен. – Yuji

+0

Отлично работает. Благодарю. – LandonSchropp

0

Так напишите это так, просто оберните его в do {} while (0) < - и не только в скобках.

#define SAFE_CAST(OBJECT, TYPE, VAR) do { \ 
    id obj = OBJECT; \ 
    VAR = [obj isKindOfClass:[TYPE class]] ? (TYPE *) obj : nil; \ 
} while(0) 
+0

Я не могу заставить это правильно компилироваться в используемом выше. Вывод компилятора: ошибка: ожидаемое выражение перед «сделать». – LandonSchropp

+0

Эх, вы правы, извините. Сначала возникает синтаксическая ошибка после nil, отсутствует; и, во-вторых, он должен стоять сам по себе. Значит, либо передайте другой аргумент, который присваивает результат тройной операции, либо делает это по-другому. :) – jer

+0

Не беспокойтесь. Спасибо за ответ в любом случае. – LandonSchropp

4

Если вы действительно думаете, что вы должны сделать это, вы могли бы использовать функцию:

#define SAFE_CAST(Object, Type) (Type *)cast_helper(Object, [Type class]) 
static id cast_helper(id x, Class c) { 
    return [x isKindOfClass:c] ? x : nil; 
} 
+0

Это интересный подход, но макрос просто казался немного чище. Я стараюсь избегать использования глобальных функций. Кроме того, не могли бы вы рассказать о том, что вы подразумеваете под «Если вы действительно думаете, что должны это делать»?Несколько других языков поддерживают безопасное литье, включая C# и C++. Почему я не должен использовать их? – LandonSchropp

+1

@helixed: почему вы думаете, что глобальный макрос, висящий вокруг, лучше, чем глобальная функция, висящая вокруг? – JeremyP

+0

@helixed: для подхода plist, упомянутого выше, он подходит, его просто та ситуация, когда эти динамические броски действительно нужны, редки. Однако я не вижу конкретной проблемы с глобальной функцией - я бы скорее использовал функции, чем макросы, которые выполняют только прямолинейную текстовую обработку. –