颤抖吧!都在我的魔法下颤抖吧!--------我是 iOS 黑魔法师!
availability(可用性)
让我们来看一个例子。
void f(void) __attribute__((availability(macos,introduced=10.4,deprecated=10.6,obsoleted=10.7)));
这个 API 包含下面几个信息:
- 只能在
macos
使用 - 在
10.4
系统之后才可以开始使用 - 在
10.6
系统之后开始废弃但是还是可以用 - 在
10.7
已经完全移除,没有这个 API。
属性列表:
-
introduced
:版本号类型开始的版本
-
deprecated
:版本号类型废弃的版本
-
obsoleted
:版本号类型移除的版本
-
unavailable
:平台类型标识那些平台不可用
-
message
: 文本类型过时时提示的文字
-
replacement
:文本类型废弃替换的文本
支持的平台列表:
ios
macos
tvos
watchos
后面声明的必须要符合前面的声明准则。
声明一个 g
函数在 MacOS10.4
之后可以用。
void g(void) __attribute__((availability(macos,introduced=10.4)));
新增在 iOS4.0
支持
void g(void) __attribute__((availability(ios,introduced=4.0)));
声明在 MacOS
和 iOS
可用。
void g(void)
下面再声明在 MacOS10.5
之后支持是错误的。
void g(void) __attribute__((availability(macos,introduced=10.5)))
如果要重写方法重写方法必须支持范围更广。
@interface A
/**
声明 method在 iOS5.0之后可以使用
*/
- (void)method __attribute__((availability(ios,introduced=5.0)));
/**
声明 method2在 iOS5.0之后可以使用
*/
- (void)method2 __attribute__((availability(ios,introduced=5.0)));
@end
@interface B : A
/**
重写父类方法 method 在 iOS4.0就可以用
*/
- (void)method __attribute__((availability(ios,introduced=4.0)));
/**
重写父类方法 method2 在 iOS6.0之后才可以用
*/
- (void)method2 __attribute__((availability(ios,introduced=6.0)));
@end
对于子类 B
重写父类 A
方法 method2
在 iOS6.0
之后才可以用是错误的,因为父类方法是可以在 iOS5.0
就可以用的。
deprecated(过时)
支持语法:
- 函数
- 变量
- 类型
void function(void) __attribute__((deprecated("已经过时请用function2方法","function2")));
参数:
- 第一个参数: 过时的提示语
- 第二个参数 : 让编译器替换的内容
当我们调用 function
方法的时候编译器会提示警告,并且提示我们修复。
当我们点击 Fix-it
时候编译器会自动把第二个参数修复掉。
我认为这个属性是我们编程过程中一个不错的功能属性。
objc_boxable(封箱)
标有objc_boxable属性的 struct
和 union
可以与Objective-C boxed表达式语法 @(...)
一起使用。
struct __attribute__((objc_boxable)) struct1 {
int i;
};
struct struct_demo demo;
NSValue *value = @(demo);
objc_requires_super(子类必须调用父类)
一些Objective-C类允许一个子类覆盖父类中的一个特定方法,但是期望覆盖方法也会调用父类中的被覆盖的方法。对于这些情况,我们提供一个属性来指定一个方法需要在子类中的覆盖方法中调用super
。
__attribute__((objc_requires_super))
这个属性放在方法的尾部。
让我们创建 TestOne
类声明一个方法 Function
实现这个属性。
@interface TestOne : NSObject
- (void)function __attribute__((objc_requires_super));
@end
我们创建一个 TestTwo
的类重写 Function
。
@implementation TestTwo
- (void)function {
}
@end
我们可以看到编译器曝出一个警告类似这样。
这样子类只要重写父类就要调用父类的方法了。
- (void)function {
[super function];
}
运行时名称
默认情况下,Objective-C接口或协议标识符用于该对象的元数据名称。objc_runtime_name属性允许注释的接口或协议在对象的元数据名称中使用指定的字符串参数,而不是默认名称。
__attribute__((objc_runtime_name("MyLocalName")))
在 @protocol
和 @interface
声明之前声明。
我们在类 TestOne
实现这个属性。
__attribute__((objc_runtime_name("TestThree")))
@interface TestOne : NSObject
- (void)function __attribute__((objc_requires_super));
@end
让我们 AppDelegate
创建一个 TestOne
输出一下看看输出什么。
TestOne *one = [[TestOne alloc] init];
NSLog(@"%@",one);
我们看一下控制台的输出
<TestThree: 0x600000004b50>
显示是我们替换的名字。
objc_runtime_visible(只允许在运行时可见)
该属性指定其应用于的Objective-C类对于Objective-C运行时可见,但不对链接器可见。用此属性注释的类不能被子类化,并且不能为它们定义类别。
我们给 TestTwo
类增加这个属性
__attribute__((objc_runtime_visible))
@interface TestTwo : TestOne
@end
我们新建一个类 TestFour
继承于 TestTwo
当我们 @implementation
实现的时候编译器会直接提示我们不可以子类。
overloadable (方法重载)
Clang提供对C中C ++函数重载的支持,使用 overloadable
属性引入C中的函数重载。
我们创建一个无参数无返回值重载的方法 TestFunction
void TestFunction(void) __attribute__((overloadable)){
}
我们创建一个接受 int
属性返回 int
值的重载方法 TestFunction
int TestFunction(int a) __attribute__((overloadable)) {
return a;
}
我们创建一个接受 int *
指针类型属性返回 int *
指针的重载方法 TestFunction
int * TestFunction(int *a) __attribute__((overloadable)) {
int *b = 0;
*b += *a;
return b;
}
一样的方法名称因为加上 overloadable
属性不会让编译器抱错,类似于 Swift
语言方法,可以让同一个方法接受不同的类型。
让我们调用尝试一下。
TestFunction();
TestFunction(2);
int *a;
TestFunction(a);
正常编译。
你可以试一下删除其中一个函数的 __attribute__((overloadable))
看看会提示什么。
objc_subclassing_restricted(类不可以子类化)
这个和 objc_runtime_visible
看着有点像。
我们给类 TestTwo
添加属性 objc_subclassing_restricted
__attribute__((objc_subclassing_restricted))
@interface TestTwo : TestOne
@end
我们创建一个类 TestSex
类继承于 TestTwo
@interface TestSex : TestTwo
@end
编译器会抱错提示下面的信息
@compatibility_alias (别名)
这个可以让其他的类名运行时使用其他类。
我们创建 TestSex
指向 TestTwo
__attribute__((objc_subclassing_restricted))
@interface TestTwo : TestOne
@end
@compatibility_alias TestSex TestTwo;
我们在 AppDelegate
使用
TestSex *sex = [[TestSex alloc] init];
但是 TestSex
是不存在的。你可以让老代码兼容新的框架。
不信你试一下。