Можно ли перенаправить новые литералы Clang Objective-C в пользовательские классы?

Хотя перегрузка @ начинает наступать на опасную территорию, мне нравится добавление новых литералов Objective-C в Clang 3.1. К сожалению, новые литералы имеют ограниченное использование для меня. За исключением случаев, когда код должен взаимодействовать с AppKit, я в основном отказался от использования классов Foundation в пользу моей собственной пользовательской среды (по ряду причин; большинство из которых заключается в том, что мне нужен прямой контроль над используемыми шаблонами выделения памяти по объектам).

Я всегда мог использовать некоторые хитрости во время выполнения, чтобы выдать вновь созданный объект в качестве своего пользовательского класса (и это то, что я уже должен делать с строковыми литералами объекта, поскольку только среда выполнения GCC, не относящаяся к Apple, поддерживает -fconstantstring=class флаг), но в лучшем случае это хакерская игра, которая отбрасывает все преимущества, которые я получил, заменив эквивалентный базовый класс для начала.

В отличие от литералов строковых объектов, новые литералы, реализованные в Clang, на самом деле не являются константными классами (где макет памяти жестко задан); вместо этого соответствующие сообщения отправляются в соответствующие классы для создания и инициализации нового объекта во время выполнения. Эффект не отличается от того, что вы создали объект самостоятельно. Теоретически это означает, что используемые классы и методы, вызываемые новыми литералами, не являются жестко закодированными. На практике я не могу найти способ изменить их, чтобы они указывали на мои собственные пользовательские классы и методы (на самом деле я был бы рад просто указать на пользовательский класс; указание фиктивного метода на фактический метод во время выполнения не сложно).

Когда я впервые посмотрел на это, я действительно надеялся найти набор флагов, которые можно было бы использовать для выполнения того, что я прошу, но так как я не нашел ни одного, я надеюсь, что у кого-то есть решение.

2 ответа

Решение

Вы можете заменить класс для некоторых литералов Objective C с помощью @compatibility_alias трюк с ключевыми словами.

Вот пример.

@compatibility_alias NSNumber AAA;

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

#import <Foundation/NSObject.h>

@interface  AAA : NSObject
+ (id)numberWithInt:(int)num;
@end

@implementation AAA
+ (id)numberWithInt:(int)num
{
    return  @"AAAAA!!!";    // Abused type system just to check result.
}
@end

@compatibility_alias NSNumber AAA;

Теперь Clang сделает всю работу за вас. Я подтвердил, что это работает для чисел, массивов, литералов словаря. К сожалению, строковые литералы, кажется, испускаются статически, поэтому это не сработает.

Для получения дополнительной информации о @compatibility_alias ключевое слово, смотрите здесь.

Заметка

Так как @compatibility_alias Ключевое слово - это директива компилятора, которая применяется к текущему модулю компиляции, вам нужно отделить модуль компиляции, чтобы избежать дублирования символов с помощью класса NSObject в Apple Foundation Foundation. Вот как я это сделал.

main.m

#import "test.h" // Comes before Foundation Kit.
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        NSLog(@"return of test = %@", test());
        // insert code here...
        NSLog(@"Hello, World!");

    }
    return 0;
}

test.h

id test();

test.m

#import "test.h"
#import <Foundation/NSObject.h>

@interface  
AAA : NSObject
+ (id)numberWithInt:(int)v;
+ (id)arrayWithObjects:(id*)pobj count:(int)c;
+ (id)dictionaryWithObjects:(id*)pvals forKeys:(id*)pkeys count:(int)c;
@end
@implementation AAA
+ (id)numberWithInt:(int)v
{
    return  @"AAAAA as number!!!";
}
+ (id)arrayWithObjects:(id*)pobj count:(int)c
{
    return  @"AAAAA as array!!!";
}
+ (id)dictionaryWithObjects:(id*)pvals forKeys:(id*)pkeys count:(int)c
{
    return  @"AAAAA as dictionary!!!";
}
@end



@compatibility_alias NSDictionary AAA;
@compatibility_alias NSArray AAA;
@compatibility_alias NSNumber AAA;



id test()
{
//  return  @{};
//  return  @[];
    return  @55;
}

Результат.

2013-03-23 08:54:42.793 return of test = AAAAA!!!
2013-03-23 08:54:42.796 Hello, World!

В комментариях все правильно, но для краткости:

  • Нет.

Значения Apple @{}, @[], а также @"" литералы жестко запрограммированы в Clang. Вы можете увидеть это здесь: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/NSAPI.cpp?view=markup Все это довольно модульно, то есть это не будет трудно для Злоумышленник может добавить свой собственный буквальный синтаксис... но "модульный" не означает "доступный извне". Добавление нового синтаксиса или даже перенаправление существующего синтаксиса на новые классы определенно потребует перестройки Clang самостоятельно.

Вот пост в блоге о добавлении NSURL литералы для Clang путем взлома его внутренних органов: http://www.stuartcarnie.com/2012/06/llvm-clang-hacking-part-3.html (Спасибо @Josh Caswell)


Если вы хотите использовать Objective-C++ с расширениями C++11, вы можете использовать "пользовательские литералы", которые позволяют вам писать такие вещи, как

NSURL *operator ""URL (const char *s) { return [NSURL URLWithString: @(s)]; }

int main() {
    ...
    NSURL *myurl = "ftp://foo"URL;
    ...
}

Об этом упоминалось в комментариях к блогу Майка Эша. http://www.mikeash.com/pyblog/friday-qa-2012-06-22-objective-c-literals.html Но это не очень похоже на Objective-C-ish (или очень C++ish!), и он работает только с компилятором Objective-C++11, и, как правило, не делайте этого.:)

Другие вопросы по тегам