attribute是一套编译器指令,被GNU和LLVM编译器所支持,允许对于attribute增加一些参数,做一些高级检查和优化。
attribute的语法是,在后面加两个括号,然后写属性列表,属性列表以逗号分隔。在iOS中,很多例如NS_CLASS_AVAILABLE_IOS的宏定义,内部也是通过attribute实现的。
__attribute__((attribute1, attribute2));
objc_subclassing_restricted
objc_subclassing_restricted属性表示被修饰的类不能被其他类继承,否则会报下面的错误。
错误信息:
Cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute
objc_requires_super
objc_requires_super属性表示子类必须调用被修饰的方法super,否则报黄色警告。
在一个方法中使用
- (void)test __attribute__((objc_requires_super));`
`警告信息:(不报错)
Method possibly missing a [super test] call
constructor / destructor
constructor属性表示在main函数执行之前,可以执行一些操作。destructor属性表示在main函数执行之后做一些操作。constructor的执行时机是在所有load方法都执行完之后,才会执行所有constructor属性修饰的函数。
__attribute__((constructor)) static void beforeMain() {
NSLog(@"main函数执行前执行");
}
__attribute__((destructor)) static void afterMain() {
NSLog(@"main函数执行后执行");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"main函数执行时执行");
}
return 0;
}
执行结果:
debug-objc[23391:1143291] before main
debug-objc[23391:1143291] execute main
debug-objc[23391:1143291] after main
在有多个constructor或destructor属性修饰的函数时,可以通过设置优先级来指定执行顺序。格式是attribute((constructor(1)))的方式,在属性后面直接跟优先级。
__attribute__((constructor(3))) static void beforeMain3() {
NSLog(@"after main 3");
}
__attribute__((constructor(1))) static void beforeMain1() {
NSLog(@"after main 1");
}
__attribute__((constructor(2))) static void beforeMain2() {
NSLog(@"after main 2");
}
在constructor中根据优先级越低,执行顺序越高。而destructor则相反,优先级越高则执行顺序越高。
overloadable
总所周知,oc中不像java,c++一样可以实现函数重载。overloadable属性允许定义多个同名但不同参数类型的函数,在调用时编译器会根据传入参数类型自动匹配函数。
NSString * __attribute__((overloadable)) mytest(NSString *x) {return @"aa";}
NSString * __attribute__((overloadable)) mytest(NSNumber *x) {return @"bb";}
NSString * __attribute__((overloadable)) mytest(NSDictionary *x) {return @"cc";}
NSString * __attribute__((overloadable)) mytest(int x) { return @"int"; }
NSString * __attribute__((overloadable)) mytest(double x) { return @"double"; }
NSString * __attribute__((overloadable)) mytest(long x) { return @"long"; }
NSLog(@"%@",self.string);
NSLog(@"%@",mytest(@(1)));
objc_runtime_name
objc_runtime_name属性可以在编译时,将Class或Protocol指定为另一个名字,并且新名字不受命名规范制约,可以以数字开头
__attribute__((objc_runtime_name("123Object")))
@interface Object : NSObject
@end
NSLog(@"%@", NSStringFromClass([self class]));
执行结果:
123Object
这个属性可以用来做代码混淆,例如写一个宏定义,宏定义内部实现混淆逻辑。例如通过MD5对Object做混淆,32位的混淆结果就是497031794414a552435f90151ac3b54b,谁能看出来这是什么类。如果怕彩虹表匹配出来,再增加加盐逻辑。
cleanup
通过cleanup属性,可以指定给一个变量,当变量释放之前执行一个函数。指定的函数执行的时间,是在dealloc之前的。在指定的函数中,可以传入一个形参,参数就是cleanup修饰的变量,形参是一个地址。
#import <Foundation/Foundation.h>
static void releaseBefore(NSObject **object) {
NSLog(@"%@", *object);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *object __attribute__((cleanup(releaseBefore))) = [[NSObject alloc] init];
}
return 0;
}
如果遇到同一个代码块中,同时出现多个cleanup属性时,在代码块作用域结束时,会以添加的顺序进行调用
unused
还有一个属性很实用,在项目里经常会有未使用的变量,会报一个黄色警告。有时候可能会通过其他方式获取这个对象,所以不想出现这个警告,可以通过unused属性消除这个警告。
NSObject *object __attribute__((unused)) = [[NSObject alloc] init];
系统中已提供的一些方法
在系统里也大量使用了attribute关键字,只不过系统不会直接在外部使用attribute,一般都是将其定义为宏定义,以宏定义的形式出现在外面。
// NSLog
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
// 必须调用父类的方法
#define NS_REQUIRES_SUPER __attribute__((objc_requires_super))
// 指定初始化方法,必须直接或间接调用修饰的方法
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
网友评论