attribute是GNU C特色之一,在iOS用的比较广泛.系统中有许多地方使用到. attribute可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute)等.
书写格式:attribute后面会紧跟一对原括弧,括弧里面是相应的attribute参数
__attribute__(xxx)
官方例子:NSLog
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。对于format参数的使用如下
format (archetype, string-index, first-to-check)
第一参数需要传递“archetype”指定是哪种风格,这里是 NSString;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定第一个可变参数所在的索引.
官方例子: abort() 和 exit()
该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。
官方例子:
-
- (
CGSize)sizeWithFont:(
UIFont *)font
NS_DEPRECATED_IOS(
2_0,
7_0,
"Use -sizeWithAttributes:") __TVOS_PROHIBITED;
-
-
//来看一下 后边的宏
-
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
-
-
define
CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __attribute__((availability(ios,introduced=_iosIntro,deprecated=_iosDep,message=
"" __VA_ARGS__)))
-
-
//宏展开以后如下
-
__attribute__((availability(ios,introduced=
2_0,deprecated=
7_0,message=
""__VA_ARGS__)));
-
//ios即是iOS平台
-
//introduced 从哪个版本开始使用
-
//deprecated 从哪个版本开始弃用
-
//message 警告的消息
availability属性是一个以逗号为分隔的参数列表,以平台的名称开始,包含一些放在附加信息里的一些里程碑式的声明。
introduced:第一次出现的版本。
deprecated:声明要废弃的版本,意味着用户要迁移为其他API
obsoleted: 声明移除的版本,意味着完全移除,再也不能使用它
unavailable:在这些平台不可用
message:一些关于废弃和移除的额外信息,clang发出警告的时候会提供这些信息,对用户使用替代的API非常有用。
这个属性支持的平台:ios,macosx。
简单例子:
-
//如果经常用,建议定义成类似系统的宏
-
- (
void)oldMethod:(
NSString *)string __attribute__((availability(ios,introduced=
2_0,deprecated=
7_0,message=
"用 -newMethod: 这个方法替代 "))){
-
NSLog(
@"我是旧方法,不要调我");
-
}
-
-
- (
void)newMethod:(
NSString *)string{
-
NSLog(
@"我是新方法");
-
}
效果:
//如果调用了,会有警告
告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过init来初始化,只能通过特定的初始化方法()比如单例,就可以将init方法标记为unavailable;
-
//系统的宏,可以直接拿来用
-
#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))
-
-
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
-
@interface Person : NSObject
-
-
@property(
nonatomic,
copy)
NSString *name;
-
-
@property(
nonatomic,
assign)
NSUInteger age;
-
-
- (
instancetype)init
NS_UNAVAILABLE;
-
-
- (
instancetype)initWithName:(
NSString *)name age:(
NSUInteger)age;
-
-
@end
//实际上unavailable后面可以跟参数,显示一些信息,如:
-
//系统的
-
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
表示这个类是一个根类(基类),比如NSObject,NSProxy.
-
//摘自系统
-
//NSProxy
-
NS_ROOT_CLASS
-
@interface NSProxy <NSObject> {
-
Class isa;
-
}
-
-
//NSObject
-
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
-
OBJC_ROOT_CLASS
-
OBJC_EXPORT
-
@interface NSObject <NSObject> {
-
Class isa OBJC_ISA_AVAILABILITY;
-
}
@property (nonatomic,strong) __attribute__((NSObject)) CFDictionaryRef myDictionary;
CFDictionaryRef属于CoreFoundation框架的,也就是非OC对象,加上attribute((NSObject))后,myDictionary的内存管理会被当做OC对象来对待.
用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用super类的 designated initializer,编译器会发出警告。可以简写成NS_DESIGNATED_INITIALIZER
这篇文章讲的很好,建议参考这个.
https://yq.aliyun.com/articles/5847
语法:
__attribute__((visibility("visibility_type")))
其中,visibility_type 是下列值之一:
default
假定的符号可见性可通过其他选项进行更改。缺省可见性将覆盖此类更改。缺省可见性与外部链接对应。
hidden
该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。
internal
除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。
protected
该符号存放在动态符号表中,但定义模块内的引用将与局部符号绑定。也就是说,另一模块无法覆盖该符号。
除指定 default 可见性外,此属性都可与在这些情况下具有外部链接的声明结合使用。
您可在 C 和 C++ 中使用此属性。在 C++ 中,还可将它应用于类型、成员函数和命名空间声明。
系统用法:
-
// UIKIT_EXTERN extern
-
#ifdef __cplusplus
-
#define UIKIT_EXTERN extern "C" __attribute__((visibility ("default")))
-
#else
-
#define UIKIT_EXTERN extern __attribute__((visibility ("default")))
-
#endif
-
-
`
编译器对函数参数进行NULL的检查,参数类型必须是指针类型(包括对象)
//使用
-
- (
int)addNum1:(
int *)num1 num2:(
int *)num2 __attribute__((
nonnull (
1,
2))){
//1,2表示第一个和第二个参数不能为空
-
return *num1 + *num2;
-
}
-
-
- (
NSString *)getHost:(
NSURL *)url __attribute__((
nonnull (
1))){
//第一个参数不能为空
-
return url.host;
-
}
__attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐.例如:
不加修饰的情况
-
typedef
struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}Family;
-
-
//输出字节:
-
NSLog(
@"Family size is %zd",
sizeof(Family));
-
//输出结果为:
-
2016
-07
-25
10:
28:
45.380 Study[
917:
436064] Family size is
12
//修改字节对齐为1
-
typedef
struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}__attribute__ ((aligned (
1))) Family;
-
-
//输出字节:
-
NSLog(
@"Family size is %zd",
sizeof(Family));
-
//输出结果为:
-
2016
-07
-25
10:
28:
05.315 Study[
914:
435764] Family size is
12
和上面的结果一致,因为 设定的字节对齐为1.而结构体中成员的最大字节数是int 4个字节,1 < 4,按照4字节对齐,和系统默认一致.
修改字节对齐为8
-
typedef
struct
-
{
-
char member1;
-
int member2;
-
short member3;
-
}__attribute__ ((aligned (
8))) Family;
-
-
//输出字节:
-
NSLog(
@"Family size is %zd",
sizeof(Family));
-
//输出结果为:
-
2016
-07
-25
10:
28:
05.315 Study[
914:
435764] Family size is
16
这里 8 > 4,按照8字节对齐,结果为16,不知道字节对齐的可以看我的这篇文章http://www.jianshu.com/p/f69652c7df99
可是想了半天,也不知道这玩意有什么用,设定值小于系统默认的,和没设定一样,设定大了,又浪费空间,效率也没提高,感觉学习学习就好.
让指定的结构结构体按照一字节对齐,测试:
-
//不加packed修饰
-
typedef
struct {
-
char version;
-
int16_t sid;
-
int32_t len;
-
int64_t time;
-
} Header;
-
-
//计算长度
-
NSLog(@
"size is %zd",
sizeof(Header));
-
输出结果为:
-
2016
-07
-22
11:
53:
47.728 Study[
14378:
5523450] size is
16
可以看出,默认系统是按照4字节对齐
-
//加packed修饰
-
typedef
struct {
-
char version;
-
int16_t sid;
-
int32_t len;
-
int64_t time;
-
}__attribute__ ((packed)) Header;
-
-
//计算长度
-
NSLog(@
"size is %zd",
sizeof(Header));
-
输出结果为:
-
2016
-07
-22
11:
57:
46.970 Study[
14382:
5524502] size is
15
用packed修饰后,变为1字节对齐,这个常用于与协议有关的网络传输中.
内联函数:内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理
内联的本质是用代码块直接替换掉函数调用处,好处是:快代码的执行,减少系统开销.适用场景:
使用例子:
-
//函数声明
-
void test(int a) __attribute__((always_inline));
当函数或者方法的返回值很重要时,要求调用者必须检查或者使用返回值,否则编译器会发出警告提示
-
- (BOOL)
availiable __attribute__((warn_unused_result))
-
{
-
return
10;
-
}
警告如下:
因为某些原因,我们不希望这个类被继承,也就是 "最终"的类,用法如下:
-
__attribute__((objc_subclassing_restricted))
-
@interface ViewController : UIViewController
-
-
-
@end
如果继承了这个类,编译器会报错
这个属性要求子类在重写父类的方法时,必须要重载父类方法,也就是调用super方法,否则警告.示例如下:
-
@interface ViewController : UIViewController
-
-
- (
void)jump __attribute__((objc_requires_super));
-
-
@end
-
-
- (
void)jump{
-
NSLog(
@"父类必须先执行");
-
}
-
-
-
@interface SGViewController : ViewController
-
-
@end
-
-
@implementation SGViewController
-
- (
void)jump{
-
NSLog(
@"子类才能再执行");
-
}
-
@end
警告如下:
实现类似于NSNumber 的快速打包能力@(...),一般对于struct,union我们只能通过NSValue将其打包. objc_boxable 可以帮助我们实现快速打包,示例如下:
-
//自定义结构体
-
typedef
struct __attribute__((objc_boxable)){
-
CGFloat x,y,width,height;
-
}SGRect;
-
-
SGRect rect = {
0,
0,
100,
200};
-
//这里直接打包成NSValue
-
NSValue *value = @(rect);
-
-
//这里我直接用系统的方法打印
-
NSLog(
@"%@",
NSStringFromCGRect(value.CGRectValue));
-
-
输出:
-
2016
-07
-21
21:
28:
43.538 Study[
14118:
5408921] {
{
0,
0}, {
100,
200}}
这样SGRect就具备快速打包功能了.
意思是: 构造器和析构器;constructor修饰的函数会在main函数之前执行,destructor修饰的函数会在程序exit前调用.
示例如下:
-
int main(
int argc,
char * argv[]) {
-
@autoreleasepool {
-
NSLog(
@"main");
-
return
UIApplicationMain(argc, argv,
nil,
NSStringFromClass([AppDelegate
class]));
-
}
-
}
-
-
__attribute__((constructor))
-
void before(){
-
NSLog(
@"before main");
-
}
-
-
__attribute__((destructor))
-
void after(){
-
NSLog(
@"after main");
-
}
-
-
//在viewController中调用exit
-
- (
void)viewDidLoad {
-
[
super viewDidLoad];
-
-
exit(
0);
-
}
-
输出如下:
-
-
2016
-07
-21
21:
49:
17.446 Study[
14162:
5415982] before main
-
2016
-07
-21
21:
49:
17.447 Study[
14162:
5415982] main
-
2016
-07
-21
21:
49:
17.534 Study[
14162:
5415982] after main
注意点:
实际上如果存在多个修饰过的函数,可以它们的调整优先级
代码如下:
-
int main(
int argc,
char * argv[]) {
-
@autoreleasepool {
-
NSLog(
@"main");
-
return
UIApplicationMain(argc, argv,
nil,
NSStringFromClass([AppDelegate
class]));
-
}
-
}
-
-
__attribute__((constructor(
101)))
-
void before1(){
-
NSLog(
@"before main - 1");
-
}
-
__attribute__((constructor(
102)))
-
void before2(){
-
NSLog(
@"before main - 2");
-
}
-
-
__attribute__((destructor(
201)))
-
void after1(){
-
NSLog(
@"after main - 1");
-
}
-
__attribute__((destructor(
202)))
-
void after2(){
-
NSLog(
@"after main - 2");
-
}
-
-
输出结果如下:
-
2016
-07
-21
21:
59:
35.622 Study[
14171:
5418393] before main -
1
-
2016
-07
-21
21:
59:
35.624 Study[
14171:
5418393] before main -
2
-
2016
-07
-21
21:
59:
35.624 Study[
14171:
5418393] main
-
2016
-07
-21
21:
59:
35.704 Study[
14171:
5418393] after main -
2
-
2016
-07
-21
21:
59:
35.704 Study[
14171:
5418393] after main -
1
注意点:
当函数声明和函数实现分开写时,格式如下:
-
static void before() __attribute__((
constructor));
-
-
static void before()
{
-
printf("before\n");
-
}
讨论:+load,constructor,main的执行顺序,代码如下:
-
+ (void)load
{
-
NSLog(@"load");
-
}
-
__attribute__((
constructor))
-
void
before
()
{
-
NSLog(@"before main");
-
}
-
-
输出结果如下:
-
2016-
07-
21
22:
13:
58.591 Study[
14185:
5421811] load
-
2016-
07-
21
22:
13:
58.592 Study[
14185:
5421811] before main
-
2016-
07-
21
22:
13:
58.592 Study[
14185:
5421811] main
可以看出执行顺序为:
load->constructor->main
为什么呢?
因为 dyld(动态链接器,程序的最初起点)在加载 image(可以理解成 Mach-O 文件)时会先通知 objc runtime 去加载其中所有的类,每加载一个类时,它的 +load 随之调用,全部加载完成后,dyld 才会调用这个 image 中所有的 constructor 方法,然后才调用main函数.
用来检查参数是否合法,只能用来修饰函数:
-
void printAge(int age)
-
__attribute__
((enable_if(age > 0 && age < 120, "你丫太监?")))
-
{
-
NSLog(@
"%d",age);
-
}
表示只能输入的参数只能是 0 ~ 120左右,否则编译报错
报错如下:
声明到一个变量上,当这个变量作用域结束时,调用指定的一个函数.如果不知道什么是作用域,请先学习一下.例子:
-
//这里传递的参数是变量的地址
-
void intCleanup(int *num){
-
NSLog(@
"cleanup------%d",*num);
-
}
-
-
- (
void)test{
-
int a __attribute__((cleanup(intCleanup))) =
10;
-
}
-
-
输出结果为:
-
2016-
07-
22
09:
59:
09.139 Study[
14293:
5495713] cleanup------
10
注意点:
示例代码:
-
void intCleanup(
int *num){
-
NSLog(
@"cleanup------%d",*num);
-
}
-
-
void stringCleanup(
NSString **str){
-
NSLog(
@"cleanup------%@",*str);
-
}
-
-
void rectCleanup(
CGRect *rect){
-
CGRect temp = *rect;
-
NSString *str =
NSStringFromCGRect(temp);
-
NSLog(
@"cleanup------%@",str);
-
}
-
-
-
int a __attribute__((cleanup(intCleanup))) =
10;
-
{
-
NSString *string __attribute__((cleanup(stringCleanup))) =
@"string";
-
CGRect rect __attribute__((cleanup(rectCleanup))) = {
0,
0,
1,
1};
-
}
-
-
-
输出结果为:
-
2016
-07
-22
10:
09:
36.621 Study[
14308:
5498861] cleanup------{
{
0,
0}, {
1,
1}}
-
2016
-07
-22
10:
09:
36.622 Study[
14308:
5498861] cleanup------string
-
2016
-07
-22
10:
09:
36.622 Study[
14308:
5498861] cleanup-----
-10
讨论:如果修饰了某个对象,那么cleanup和dealloc,谁先执行?
测试代码如下:
-
void objectCleanup(
NSObject **obj){
-
NSLog(
@"cleanup------%@",*obj);
-
}
-
-
- (
void)viewDidLoad {
-
[
super viewDidLoad];
-
ViewController *vc __attribute__((cleanup(objectCleanup))) = [[ViewController alloc] init];
-
}
-
-
- (
void)dealloc{
-
NSLog(
@"dealloc");
-
}
-
-
输出结果如下:
-
2016
-07
-22
10:
23:
08.839 Study[
14319:
5502769] cleanup------<ViewController:
0x13fe881e0>
-
2016
-07
-22
10:
23:
08.840 Study[
14319:
5502769] dealloc
可以明显看出,cleanup先于对象的dealloc执行.
-
//指向block的指针,觉得不好理解可以用typeof
-
void blockCleanUp(void(^*block)()){
-
(*block)();
-
}
-
-
void (^block)(
void) __attribute__((cleanup(blockCleanUp))) = ^{
-
NSLog(@
"finish block");
-
};
这个好处就是,不用等到block最后才写某些代码,我们可以把它放在block的任意位置,防止忘记.
用于c语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型:
-
__attribute__((overloadable)) void
print(NSString *
string){
-
NSLog(@
"%@",
string);
-
}
-
-
__attribute__((overloadable)) void
print(
int num){
-
NSLog(@
"%d",num);
-
}
-
-
//调用
-
print(
10);
-
print(@
"哈哈");
看到runtime是不是就感觉高大上,没错这个也跟运行时有关.作用是将将类或协议的名字在编译时指定成另一个.示例如下:
-
__attribute__((objc_runtime_name(
"NSObject")))
-
@interface SGObject :NSObject
-
-
@end
-
-
//调用
-
NSLog(
@"%@",[SGObject
class]);
-
//输出
-
2016
-07
-22
11:
18:
00.934 Study[
14355:
5516261]
NSObject
可以用来做代码混淆.
更多请看官网:
https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html
文章浏览阅读257次。使用官方提供的脑PET数据集,构建逻辑回归模型来进行脑PET图像的疾病预测,数据集被分为两类,分别为轻度认知障碍(MCI)患者的脑部影像数据和健康人(NC)的脑部影像数据,图像数据格式为nii,因此本赛题可抽象为一个二分类问题。nii是一种常用的医学图像数据格式,主要用于存储和交换神经影像数据。以下是一些主要特点:1.主要用于存储3D(三维)医学图像数据,如MRI(磁共振成像)和CT(计算机断层扫描)图像。2.支持多种数据类型,使得其可以支持不同类型的数据处理和分析。_逻辑回归需要训练很多轮么
文章浏览阅读1.2k次,点赞31次,收藏26次。增强现实通常被视为一个利基领域。然而,在过去的两年里,它已经到了一个成熟的阶段,应该在一般的营销堆栈中进行考虑。正如我们所看到的,这个市场是巨大的,而且随着主要参与者向这项技术投入大量投资,它只会继续增长。从苹果到Meta,大公司都相信身临其境的未来,而想要获得成功的营销人员和创意人员也加入了进来。本文第三章,最佳设计实践除了深入讨论AR设计的原则外,还全面推荐了AI设计工具。旨在帮助读者的AI作品脱颖而出。
文章浏览阅读473次。OSI七层网络模型由下至上为1至7层,分别为:物理层(Physical layer),数据链路层(Data link layer),网络层(Network layer),传输层(Transport layer),会话层(Session layer),表示层(Presentation layer),应用层(Application layer)。1.1 应用层,很简单,就是应用程序。这一层负责_usage: ./tcp_client hostname
文章浏览阅读2.8k次。环境准备安装 maven 安装 java 环境[root@cicd-nexus ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz[root@cicd-nexus ~]# tar xf apache-maven-3.6.3-bin.tar.gz -C /usr/local/[root@cicd-nexus ~]# tar xf _maximum component age
文章浏览阅读1.6k次。使用js-xlsx handsontable 可以把本地excel 解析到网页上,然后分批次传入后台。在chrome 下 可以参考 https://github.com/SheetJS/js-xlsx 【Browser file upload form element】但需要使用FileReader api 这个只有ie10 才开始支持。兼容ie9 ,ie9需要通过flash 来支持..._js 导excel 分批写入
文章浏览阅读2.5k次。在wxWidgets中,想要通过其本身的控件来实现图片+文件的按钮,貌似不太容易做到。但是可以通过重载wxControl来自绘图片+文件按钮。下面给出的是已经封装好的按钮类:wxBitmapButtonEx.h#ifndef _BITMAP_BUTTON_EX_H#define _BITMAP_BUTTON_EX_H#include "wx/wx.h"enum eBitm_wxwidgets 中文按钮
文章浏览阅读847次。invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。postInvalidate它是向主线程发送个Message,然后handleMessage时,调用了invalidate()函数。(系统帮我们 写好了 Handle..._postinvalidate和invalidate的区别
文章浏览阅读9.1k次。excel数据进行分类汇总的步骤在做分类汇总前,我们需要对数据先进行排序,否则分类汇总无法进行。得到排序后的表格。点击上方工具栏中的“数据”→“分类汇总”。在弹出的对话框中选择“分类字段”→“汇总方式”→“决定汇总项”。点击确定出现数据汇总结果。Excel表格中求差函数公式使用的方法第一步:打开Excel表格,单击第一排,第三个“单元格”,也就是C1,在C1中输入“=A1-B1”;第二步:这个公式..._表格求差公式
文章浏览阅读1.5w次,点赞49次,收藏169次。前言好激动,断断续续装了两三天才装上,踩了好多坑。这里把成功安装的步骤详细写下来,如果有小伙伴需要,可以尝试一下,但我不能保证你也可以装好。首先说一下我的各个版本(不谈版本的安装教程都是耍流氓!)是用虚拟机软件:VirtualBOX6.1.30系统版本:ubuntu-20.04.3-desktop-amd64(最小安装模式,中文)OpenCV版本:4.5.5安装时间:2022.2.11下面是步骤1、进入OpenCV的官方下载地址Releases - OpenCV,下载So_无法定位软件包 libgazebo-dev
文章浏览阅读320次,点赞6次,收藏10次。/是上一个的进化版,相邻的可以一样但是不能都是绿色,注意条件;~~~//仅当笔者个人备忘录使用。
文章浏览阅读6.7k次。由于Install Intel x86 Emulator Accelerator (HAXM installer) (revision: 7.6.5)安装失败,导致我的安卓虚拟机无法启动。解决办法有一下几种:1.开机进入BIOS打开Virtual虚拟化功能,然后进入Androidstudio 的SDK manager里面安装HAXM2.关闭系统中的Hyper-v,进入控制面板的程序和功能,将Hyper-v去选即可。3.如果前面的方法都不行,那么建议你重新下载AndroidStudio最新版进行安装_intel x86 emulator
文章浏览阅读1.1w次,点赞2次,收藏15次。 每一种语言都有一组基本的语法约定,POWERBUILDER也不例外。 (1)断行、续行与多条语句 通常情况下,powerbuilder的一条语句是写到一行上的,该条语句在书写完毕之后,按键转到下一行,开始写下一句的内容。也就是说,在PowerBuilder中,使用键作为一行的结束。在PowerBuilder语句比较长的情况下,为了方便阅读,可以使用续行符号把一条语句写到几_powerbuilder