Clang Атрибуты Clang предназначены для предоставления аннотаций источника. Позволяет разработчикам легко выразить запрос компилятору. Вовлечение статического анализатора, изменение имени, генерация кода и другие процессы. Обычно __attribute__(xxx)
входит в код формы. для простоты использования номер общего атрибута Cocoa также определяется как макрос. например, часто встречается в системных заголовочных файлах NS_CLASS_AVAILABLE_IOS(9_0)
это __attribute__(availability(...))
простая формулировка этого свойства.
Для ознакомления с общими атрибутами см. вводную статью NSHipster и вводную статью Twitter. В этой статье также будут представлены несколько интересных атрибутов "черной магии", которые могут иметь неожиданные эффекты в некоторых сценариях.
Objc_subclassing_restricted
Используйте это свойство, чтобы определить Final Class
, скажем, класс не может быть унаследован, допустим, у нас есть имя foo
класса, но мы не хотим, чтобы кто-то мог наследовать от него:
@interfacefoo
: NSObject @end //child class @interface Child :foo
// Eunuch cannot be a child @end
Пока передний @interface добавляет objc_subclassing_restricted
это свойство:
__attribute__((objc_subclassing_restricted)) @interfacefoo
: NSObject @end //child class @interface Child :foo
// ←- Compile Error @end
Objc_requires_super
Также: NS_REQUIRES_SUPER
, при наследовании этого метода super
необходимо вызывать подкласс флага, иначе выдается предупреждение компиляции:
@interface Father : the NSObject — ( void ) hailHydra __attribute __ ((objc_requires_super)); @end @implementation Father — ( void ) hailHydra { NSLog ( @ “Hail Hydra!” ); } @end @interface Son : Father @end @implementation Son — ( void )hailHydra { } // ←- Warning missing [super hailHydra] @end
Objc_boxable
Коробка сахара синтаксиса Objective-C @(...)
может быть базовыми типами данных в NSNumber
объекте, если вы хотите тип коробки struct
или union
в NSValue
объекте, это свойство можно использовать:
Typedef struct __attribute__((objc_boxable)) { CGFloat x, y, width, height; } XXRect;
Таким образом, XXRect
у вас есть возможность быть в боксе:
CGRect rect1 = { 1 , 2 , 3 , 4 }; NSValue *value1 = @(rect1); // ←- Compile Error XXRect rect2 = { 1 , 2 , 3 , 4 }; NSValue *value2 = @(rect2 ); // ✔︎
Конструктор/деструктор
Как следует из названия, конструкторы и деструкторы. Функция этих двух свойств будет вызываться в исполняемом файле (или общей библиотеке) соответственно load и unload. main()
выполняется перед вызовом функции и возвратом:
__attribute__((constructor)) static void beforeMain( void ) { NSLog ( @”beforeMain” ); } __attribute__((destructor)) static void afterMain( void ) { NSLog ( @”afterMain” ); } int main( int argc, Const char * argv[]) { NSLog ( @”main” ); return 0 ; }
Вывод:
beforeMain main afterMain
конструктор и +load
выполняются до вызова основной функции, но +load
еще раньше, чем конструктор, потому что dyld (начальная начальная точка для динамического компоновщика, программы) загрузки изображения (можно понимать как файл Mach-O) будет сначала уведомлен, когда objc runtime
чтобы загрузить в котором все классы, каждый загружает класс, он +load
будет вызывать, после того как все загружено, dyld будет вызывать все методы конструктора в этом образе.
Итак, конструктор — отличное время, чтобы сделать что-то плохое:
- Все классы загружены
- Основная функция еще не выполнена
- Нет необходимости монтировать как + load в классе
FDStackView
Метод FDStackViewPatchEntry заключается в использовании этой возможности для реализации трюка украсть день.
PS: Если есть несколько конструкторов и вы хотите управлять приоритетом, вы можете написать
__attribute__((constructor(101)))
что чем меньше число, тем выше приоритет, 1 ~ 100 зарезервировано для системы.
Enable_if
Это свойство может использоваться только в функциях C и может использоваться для реализации статической проверки параметров:
Static void printValidAge( int age) __attribute__((enable_if(age > 0 && age < 120 , “You 丫 Martian?” ))) { printf( “%d” , age); }
Выраженная необходимость удовлетворения этой функции называется age > 0 && age < 120
разрешенной, а значит:
printValidAge( 26 ); // ✔︎ printValidAge( 150 ); // ←- Compile Error printValidAge( -1 ); // ←- Compile Error
Очистка
Объявите переменную, когда область действия переменной закончится, вызовите указанную функцию, Reactive Cocoa использует эту функцию для достижения магии @onExit.
__attribute__((cleanup(...)))
Используется для оформления переменной и автоматического выполнения указанного метода в конце его области действия, например:
// Specify a cleanup method, note // that the input parameter is the address of the modified variable, // the type should be the same // For the pointer to the objc object (id *), if it is not mandatory // to declare __strong the default is __autoreleasing, causing the // type does not match static void stringCleanUp(__ strong NSString **string) { NSLog ( @”%@” , *string); } // In a method: -(void) someMethod { __ strong NSString *string __attribute__((cleanup(stringCleanUp))) = @”sunnyxx “ ; } // automatically calls stringCleanUp when running to the end of //this scope
Область заканчивается, включая фигурные скобки, возврат, переход, разрыв, исключение и т. д.
Конечно, переменных, которые можно изменить, больше, чем NSString, настроить класс или базовый тип, все они возможны:
// Custom Class static void sarkCleanUp(__ strong Sark **sark) { NSLog ( @”%@” , *sark); } __ strong Sark *sark __attribute__((cleanup(sarkCleanUp))) = [Sark new] ; // The basic type static void intCleanUp( NSInteger *integer) { NSLog ( @”%d” , *integer); } NSInteger integer __attribute__((cleanup(intCleanUp))) = 1 ;
Если в области есть несколько переменных очистки, порядок их вызова
определяется после порядка стека; кроме того, перед этими объектами dealloc
вызывается очистка.
Перегружаемый
Для функций C вы можете определить несколько методов с одинаковым именем функции, но разными параметрами. При вызове компилятор автоматически выберет прототип функции по параметрам:
__attribute__((overloadable)) void logAnything (id obj) { NSLog(@ “%@” , obj); } __attribute__((overloadable)) void logAnything ( int number) { NSLog(@ “%@” , @(number) } ; } __attribute__((overloadable)) void logAnything (CGRect rect) { NSLog(@ “%@” , NSStringFromCGRect(rect)); } // Tests logAnything(@[@ “1” , @ “2” ]); logAnything( 233 ); logAnything(CGRectMake(1 , 2 , 3 , 4 ));
Objc_runtime_name
Используется @interface
или @protocol
имени класса или протокола к другому, указанному во время компиляции:
__attribute__((objc_runtime_name( “SarkGay” ))) @interface Sark : NSObject @end NSLog ( @”%@” , NSStringFromClass ([Sark class])); // “SarkGay”
Все места, которые используют это имя класса напрямую, будут заменены (единственное, что следует отметить, это неправильно использовать отражение во время выполнения), самое простое и грубое использование — внести путаницу в имя класса:
__attribute__((objc_runtime_name( “40ea43d7629d01e4b8d6289a132482d0dd5df4fa” ))) @interface SecretClass : NSObject @end
Это единственный известный мне атрибут, влияющий на структуру класса времени выполнения objc. Кодируя имя класса, вы можете ввести некоторую информацию во время компиляции, а затем вернуть ее в среду выполнения, а затем отменить ее. Это эквивалентно открытию секретного канала при написании кода и во время выполнения. Мозговой код, если этот атрибут определен как макрос для annotation
формы, завершает определенные функции, такие как:
@singleton wrapped __attribute__((objc_runtime_name(…))) // renamed the class name to “SINGLETON_Sark_sharedInstance” @singleton(Sark, sharedInstance) @interface Sark : NSObject + (instancetype)sharedInstance; @end
Во время выполнения с __attribute__((constructor))
gain время входа, чтобы найти класс с Runtime, обратное решение 'sharedInstance' информация селектора будет динамическим +alloc
, -init
и другими альтернативными методами, возвращает +sharedInstance
один вариант осуществления.
использованная литература
http://llvm.org/releases/3.8.0/tools/clang/docs/AttributeReference.html
http://clang-analyzer.llvm.org/annotations.html