菜单

金沙国际棋牌下载官网__attribute__ 总结

2020年3月12日 - 金沙国际棋牌下载官网
金沙国际棋牌下载官网__attribute__ 总结

Rust
1.40.0 已经正式发布。该版本的亮点包括有 #[non_exhaustive] 和 macros!() and #[attribute]s 的改进。

在本章中,我们将更多讨论有关在Visual
LISP中使用ActiveX性能。首先,我们从ActiveX的技术环境开始,包括对象、对象模型、集合、属性、方法等方面。然后我们将深挖一些特定的ActiveX技术部分。了解ActiveX功能性是学习任何语言必不可少的工作。

attribute是GNU C特色之一,在iOS用的比较广泛.系统中有许多地方使用到.
attribute可以设置函数属性(Function Attribute )、变量属性(Variable
Attribute )和类型属性(Type Attribute)等.

具体更新内容如下:

ActiveX是一种面向对象的工具,也就是说它表现为对象的使用以及对象间的关联这种方式。在这里我不想去解释面向对象的问题,这些东西最好留给那些更专业的教科书去讲。然而,我将尝试给一些基本的面向对象方面的描述,以便大家有一个基本的了解。

函数属性(Function Attribute)

#[non_exhaustive] 结构,枚举和变体

类型属性(Type Attributes)

当属性#[non_exhaustive]附加到struct或的变体时enum,它将防止定义它的板条箱外部的代码构造所述struct或变体。为了避免将来损坏,还防止其他包装箱在田地上进行彻底匹配。以下示例说明了beta取决于的错误alpha

在面向对象的环境中,任何事物都是以Classes(类)开始的。类是抽象的框架,用于描述对象应是什么样的,以及他们应该如何表现和相互联系的。在某种意义上说类定义了对象类型的种类。例如,一辆汽车是属于交通工具类。交通工具是父类,而汽车是子类。反之,你可取得更多特定的和额外定义的子类,例如旅行车、面包车和运动型轿车。

变量属性(Variable Attribute)

// alpha/lib.rs:

#[non_exhaustive]
struct Foo {
    pub a: bool,
}

enum Bar {
    #[non_exhaustive]
    Variant { b: u8 }
}

fn make_foo() -> Foo { ... }
fn make_bar() -> Bar { ... }

// beta/lib.rs:

let x = Foo { a: true }; //~ ERROR
let Foo { a } = make_foo(); //~ ERROR

// `beta` will still compile when more fields are added.
let Foo { a, .. } = make_foo(); //~ OK


let x = Bar::Variant { b: 42 }; //~ ERROR
let Bar::Variant { b } = make_bar(); //~ ERROR
let Bar::Variant { b, .. } = make_bar(); //~ OK
                   // -- `beta` will still compile...

类不处理特定的实例,他们更注重于实例方面的描述,而非使用方面。当你使用类时,你可以说调用类的一个实例。调用类的结果经常是创建了一个对象。对象可以是单独的图元或包含了更多对象的容器。

Clang特有的

幕后发生的事情是,#[non_exhaustive] struct或的构造函数的可见性enum降低到pub(crate),从而阻止了在定义它的板条箱外部进行访问。

对象
一个对象就是类的一个实例。对象有其固定的属性,同时也可以有固定的方法和事件。属性是定义对象怎样表现和反应的特性。方法是用于访问或修改对象属性或某些行为的内置函数。事件是由对象发送的通知,当执行或激活它们时,它用于响应特定动作。

书写格式

书写格式:attribute后面会紧跟一对原括弧,括弧里面是相应的attribute参数

__attribute__(xxx)

更重要的方面是,#[non_exhaustive]也可以附加到enum自身上。从标准库中获取的示例是Ordering

就用上面的汽车类的例子来说,对象可以是特别的轿车。比如说,你的轿车,带有特别的配置(制造、型号、颜色、选配、以及序列号)。你可以说,你的轿车是汽车类的一个实例,或汽车类派生的一些类。

常见的系统用法

#[non_exhaustive]
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }

图3-1 对象模型

format

官方例子: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”指定第一个可变参数所在的索引.

#[non_exhaustive]在此上下文中的目的是确保可以随时间添加更多变体。这是通过防止其他包装箱从详尽模式实现match-ing上Ordering。也就是说,编译器将拒绝:

对象模型
对象模型是一个随意的架构,或类之间的分层定义的排列关系,意思就是可以从更高级别的类来获得一个对象。对象模型与访问它的语言或工具无关,它有自己的逻辑框架。不管你是使用Visual
Basic、VBA、Visual
LISP、Delphi、Java、C/C++、C#.NET或带有ActiveX接口的其它语言,它都将以相同的模型存在。这并不代表对象模型的所有特性都支持所有语言。某些特性在某些语言中可以访问或比较容易被访问,但在其它语言中可能就不行。

noreturn

官方例子: abort() 和 exit()

该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。

match ordering {
    // This is an error, since if a new variant is added,
    // this would suddenly break on an upgrade of the compiler.
    Relaxed | Release | Acquire | AcqRel | SeqCst => {
        /* logic */
    }
}

我们将对象模型比喻成一套房子,它由房间、门和窗组成。不同的人都可进入和使用房子,而他们都是面对同样的这套房子。在这种情况下,房子和房间就是对象模型,而人即是编程语言。这样,你应该懂吧。

availability

官方例子:

- (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属性是一个以逗号为分隔的参数列表,以平台的名称开始,包含一些放在附加信息里的一些里程碑式的声明。

简单例子:

//如果经常用,建议定义成类似系统的宏
- (void)oldMethod:(NSString *)string __attribute__((availability(ios,introduced=2_0,deprecated=7_0,message="用 -newMethod: 这个方法替代 "))){
    NSLog(@"我是旧方法,不要调我");
}

- (void)newMethod:(NSString *)string{
    NSLog(@"我是新方法");
}

效果:

金沙国际棋牌下载官网 1

Paste_Image.png

//如果调用了,会有警告

金沙国际棋牌下载官网 2

Paste_Image.png

取而代之的是,其他板条箱需要通过添加通配符来解决更多变体的可能性,例如_

类继承
对象模型通常都是从根或源对象开始。在AutoCAD中,源对象是AutoCAD
Application
(AutoCAD应用程序)对象,也被称之为AcadApplication对象。它提供了基本的属性、方法、事件和来自所有其它对象和集合构成的集合。例如,AcadApplication对象有一集合为Documents(即Documents集合),在其中对应有一个或多个Document对象。每一Document对象有它自己的对象、集合、属性和方法以及其它东西。

unavailable

告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过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

金沙国际棋牌下载官网 3

Paste_Image.png

//实际上unavailable后面可以跟参数,显示一些信息,如:

//系统的
 #define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
match ordering {
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ }
    // OK; if more variants are added, nothing will break.
    _ => { /* logic */ }
}

你可向下层浏览对象模型进入下一层次的对象和集合,也可以向上层浏览父对象和集合。模型非常强大,应用程序可直接访问和操作环境来执行几乎无限的任务。它同时保持每一事物都整齐有序,在开发软件解决方案时能提供有效的帮助。

objc_root_class

表示这个类是一个根类(基类),比如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;
}

有关该#[non_exhaustive]属性的更多详细信息,可参见稳定性报告。

集合和词典
集合是在相同父窗口中一组相似的对象。该容器有一个独特的名字,在大多数情况下,将提供自己的方法来访问所包含的对象。词典是一种特殊类型的集合,它允许你扩展你自己的词典。Visual
LISP并没有过多提供用于创建或处理集合的方法。它允许你遍历它、修改项目、添加或删除条目。词典允许你添加自己的词典并写入数据,你可遍历它、添加、修改和删除其条目,同样,你也可以添加
、修改和删除词典本身。

NSObject

@property (nonatomic,strong) __attribute__((NSObject)) CFDictionaryRef myDictionary;

CFDictionaryRef属于CoreFoundation框架的,也就是非OC对象,加上attribute((NSObject))后,myDictionary的内存管理会被当做OC对象来对待.

Macro and attribute 的改进

在AutoCAD中的一些公共的集合有Documents(文档)、Layers(图层)、Dimension
Styles(标注样式)、Linetypes(线型)、 Blocks(块)等等。

objc_designated_initializer

用来修饰类的designated
initializer初始化方法,如果修饰的方法里没有调用super类的 designated
initializer,编译器会发出警告。可以简写成NS_DESIGNATED_INITIALIZER

这篇文章讲的很好,建议参考这个.
https://yq.aliyun.com/articles/5847

在AutoCAD中的一些公共的词典有PageSetups (页面设置)、Layouts
(布局)(它同样也做为词典保存),还有Express Tools中的个别组件,如WipeOuts
(遮罩)。Xrecord对象也保存在词典内。

visibility

语法:

__attribute__((visibility("visibility_type")))

其中,visibility_type 是下列值之一:

系统用法:

//  UIKIT_EXTERN     extern
 #ifdef __cplusplus
 #define UIKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
 #else
 #define UIKIT_EXTERN            extern __attribute__((visibility ("default")))
 #endif

例如,用户可以编写以下类型:Foo = expand_to_type!(bar); 其中
expand_to_type 将是一个 procedural macro。

属性、方法和事件
属性只是描述关联于对象或集合的特性。例如它可包含名称、高度、宽度、旋转角度、比例缩放、颜色、图层、线型等等。属性根据不同的对象的类型而有所区别,但有些属性对所有对象和集合是通用的。集合和词典通常提供了Count和Name属性,还有Item和Add方法。只有词典提供了Delete方法,而你却不能通过Visual
LISP删除集合。

nonnull

编译器对函数参数进行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;
}

方法是对象提供的用于访问或编辑专用特性或针对对象本身执行特别动作的内置函数。常见的方法有Rotate(旋转)、Erase(删除)、Copy(复制)、Scale(比例缩放)和Offset(偏移)。你可能注意到,这些看起来就像AutoCAD编辑命令。嗯,在本质上是这样的,但略有不同。

常见用法

包括有bang!() macros, 例如:

然而一般的AutoCAD编辑命令,必须在每步执行中验证对象。而方法是由主对象提供的,因此,只能由每一对象单独提供支持的方法。晕了吧?

aligned

__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

可是想了半天,也不知道这玩意有什么用,设定值小于系统默认的,和没设定一样,设定大了,又浪费空间,效率也没提高,感觉学习学习就好.

macro_rules! make_item { ($name:ident) => { fn $name(); } }

extern {
    make_item!(alpha);
    make_item!(beta);
}

另一种方式,OFFSET(偏移)命令可以在任何时间使用,但如果你试图偏移一个文本对象,
AutoCAD就会出现出错信息。然而,文本对象本身提供的各种方法,如Copy(复制)、Rotate(旋转)、Scale(比例缩放)和Move(移动),但没有Offset(偏移)。所以你可以从对象“调用”各种方法,却需要确定它对使用的对象是有效的。

packed

让指定的结构结构体按照一字节对齐,测试:

//不加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字节对齐,这个常用于与协议有关的网络传输中.

Procedural macro attributes on items
in extern { ... } blocks 现在也被支持:

事件是对象或集合由各种可以被检测到的或可以响应的活动所产生动作。当事件与这些事件的反应相结合使用时,就称为事件驱动编程。AutoCAD提供了一个被称之为反应器的强大的事件反应工具集,它使你可在绘图环境中发送触发器来响应各种事件。例如,当对象在活动的图形中被删除时,您可以创建一个反应器响应一个Erase事件。这只是一个事件和反应器的例子。

noinline & always_inline

内联函数:内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理

内联的本质是用代码块直接替换掉函数调用处,好处是:快代码的执行,减少系统开销.适用场景:

使用例子:

//函数声明
void test(int a) __attribute__((always_inline));
extern "C" {
    // Let's assume that this expands to `fn foo();`.
    #[my_identity_macro]
    fn foo();
}

属性关联
有一点很重要,就是你决不需要知道哪些属性适用于哪些对象和集合。有两个特别的函数对于保证您的代码能够在运行时妥善处理属性和方法的执行:(vlax-property-available-p)和(vlax-method-applicable-p)。这两个函数只是两个Visual
LISP判断函数,它提供布尔测试判断条件是真或假(LISP术语为non-nil或nil)。

warn_unused_result

当函数或者方法的返回值很重要时,要求调用者必须检查或者使用返回值,否则编译器会发出警告提示

- (BOOL)availiable __attribute__((warn_unused_result))
{
   return 10;
}

警告如下:

金沙国际棋牌下载官网 4

Paste_Image.png

这些函数的语法如下:
(vlax-property-available-p object property) 
(vlax-method-applicable-p object method) 

objc_subclassing_restricted

因为某些原因,我们不希望这个类被继承,也就是 “最终”的类,用法如下:

__attribute__((objc_subclassing_restricted))
@interface ViewController : UIViewController


@end

如果继承了这个类,编译器会报错

金沙国际棋牌下载官网 5

Paste_Image.png

目前,函数式(mac!())和属性(#[mac])macros
都可以生成macro_rules!项目。

属性是跟与之关联的对象的类型相关。例如,一个Circle(圆)对象有一个Diameter(直径)属性,但Line
(线)对象就没有。根据不同的对象类型,属性也各不一样,下面的代码在拾取CIRCLE(圆)图元时会出错:

objc_requires_super

这个属性要求子类在重写父类的方法时,必须要重载父类方法,也就是调用super方法,否则警告.示例如下:

@interface ViewController : UIViewController

- (void)jump __attribute__((objc_requires_super));

@end

- (void)jump{
    NSLog(@"父类必须先执行");
}


@interface SGViewController : ViewController

@end

@implementation SGViewController
- (void)jump{
    NSLog(@"子类才能再执行");
}
@end

警告如下:

金沙国际棋牌下载官网 6

Paste_Image.png

(if (setq ent (entsel
“n选择对象以获取其属性: “))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (princ
      (strcat “n长度: ” (vla-get-Length obj))
    )
  )
)

objc_boxable

实现类似于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 / destructor

意思是:
构造器和析构器;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("beforen");
}

讨论:+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函数.

macro_rules! accept_meta { ($m:meta) => {} }
accept_meta!( my::path );
accept_meta!( my::path = "lit" );
accept_meta!( my::path ( a b c ) );
accept_meta!( my::path [ a b c ] );
accept_meta!( my::path { a b c } );

(if (setq ent (entsel
“n选择对象以获取其属性: “))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (if (vlax-property-available-p obj ‘Length)
      (princ
        (strcat “n长度: ” (vla-get-Length obj))
      )
      (princ “n对象没有 LENGTH 属性…”)
    )
  )
)

enable_if

用来检查参数是否合法,只能用来修饰函数:

void printAge(int age)
__attribute__((enable_if(age > 0  && age < 120, "你丫太监?")))
{
    NSLog(@"%d",age);
}

表示只能输入的参数只能是 0 ~ 120左右,否则编译报错
报错如下:

金沙国际棋牌下载官网 7

Paste_Image.png

标准库中增加的 const fn

不幸的是,没有直接的方法获取给定对象的所有属性的列表来用于遍历编程。不过,你还是可以获取列表信息的,这样对你的帮助也不算小。

cleanup

声明到一个变量上,当这个变量作用域结束时,调用指定的一个函数.如果不知道什么是作用域,请先学习一下.例子:

//这里传递的参数是变量的地址
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的任意位置,防止忘记.

此版本中,以下函数成为const fn

要查询在给定的对象上有什么属性和方法,可在该对象上使用(vlax-dump-object)函数。函数的语法是(vlax-dump-object object
show-methods), show-methods参数可以是nil或非nil。如果是非nil,就显示对象支持的方法,否则就不显示方法。

overloadable

用于c语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型:

__attribute__((overloadable)) void print(NSString *string){
    NSLog(@"%@",string);
}

__attribute__((overloadable)) void print(int num){
    NSLog(@"%d",num);
}

//调用
print(10);
print(@"哈哈");

图 3-2 – Documents集合的属性和方法 

objc_runtime_name

看到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

增加到标准库的函数

图3-2显示了Documents集合对象的属性和方法。你可看到输出的第一行显示了带有一个描述的内部对象参照(IAcadDocuments),并且列出了可用的属性和方法。

以下函数和宏已经稳定:

提示! 以下的命令定义对显示所选图元的属性和方法会有用处。它没有提供出错处理,但是它仍是个有用的小工具。

(defun C:DUMP (/ ent obj)
  (while (setq ent (entsel “n选择图元以获取对象数据: “))
    (setq obj (vlax-ename->vla-object (car ent)))
    (vlax-dump-object obj T)
    (vlax-release-object obj)
  )
  (princ)
)

详情可查看更新说明:

提示! 属性后面的括号(RO)代表该属性为只读,在这里,所有的属性均为只读,方法后面括号内的数字表示了每一方法所需要的参数数目。

要访问属性,可使用(vla-get-xxx)函数,或更通用的(vlax-get-Property)函数,两者都可以。第一个函数的语法是(vla-get-xxx
object),其中XXX是属性名。在使用(vlax-get-property)函数时,语法为(vlax-get-property object
propertyname),其中propertyname可以是双引号字符串或单引号字符串。

(文/开源中国)    

(vlax-get-property object property) 或 
(vla-get-property object) 或 
(vlax-get object property) 

返回值是分配给该对象的指定名称属性的值,如果该对象的指定属性不存在,就会产生错误。举例来说,如果你对Line(直线)图元查询“Diameter”(直径)属性,就会产生错误。

参数: 
Object – 一个 vla-对象 
Property – 与对象相关的有效属性。 

示例: 
(vlax-get-property objLine “Length”) 
(vlax-get-property objLine ‘Length) 
(vla-get-Length objLine) 

所有这些语句都将完成同样的事情。

属性的名称并不要求区分大小写,但本书的例子都将首字母大写,以示清晰。总的来说,你会发现以上前两项都很好用,但也有情况是要求用到后两项。特别是涉及到象Microsoft  Excel
或 Word这样的外部应用程序。第四种方式 vlax-get 是R14版遗留下来的,只为了向后兼容而已。

使用方法
通过图3-2的例子,你可看到Documents集合对象支持4种方法:Add、Close、Item和Open。Item方法需要使用一个参数(也就是在图3-2中显示在方法后面的那个(1)),它是由集合所引出的文档的索引和命名。

通常Item方法有一个有趣的特性,那就是它可以接受字符串或整数值参数。当给的是整数参数时,它就简单地返回集合的第(nth)条目,0为第一条目。当给的是一个字符串值时,它尝试通过它的名称属性来取得条目。Item
(名称)方法不分大小写,这是相当有用的,它不需要先对字符串进行大小写转换就可以接受名称。

提示! 如果你熟悉Visual
Basic或VBA并经常使用默认方法和默认属性,那你要注意,这种特性在Visual
LISP是不存在的。例如,在Visual Basic中,通过以下任意一种途径来访问Item方法都是可行的:
Object.Item(12) 或 Object(12) 或
Object(“Name”) 

这是因为在VB或VBA中,Item方法是大多数对象的默认方法。Visual
LISP并不支持这种特性,它需要在每次使用时都阐明所有属性和方法。例如:

(vlax-invoke-method documents “Item”
12) 会有效果… 
(vla-item documents
“Drawing1.dwg”) 会有效果… 
(vlax-invoke-method documents
12) 不会有效果。 

使用图3-2的例子,Item方法可通过以下任何方法使用:

(vla-Item documents 1) 
(vla-Item documents “Drawing1.dwg”) 
(vlax-invoke-method documents “Item” 1) 
(vlax-invoke-method documents ‘Item “Drawing1.dwg”) 

(vlax-invoke-method object method [arguments]…) 或 
(vla-method object arguments) 或 
(vlax-invoke object method [arguments] …) 

调用关联于对象的方法并为方法提供任何所需的参数。如果成功,将返回结果。如果对象没有提供对应的方法,将会产生ActiveX错误。例如,在Text(文本)图元中调用“Offset”(偏移)方法,将会产生ActiveX错误。

参数: 
Object – 一个vla-对象 
Method – 由对象显露的方法 
Arguments – 支持该方法的任何所需的参数 

示例: 
(vlax-invoke-method objLine “Move” point1
point2) 
(vla-Move objLine point1 point2) 
(vlax-invoke objLine “Move” point1 point2) 

所有这些示例做的事情都一样。对于大部分AutoCAD对象来说都是可行的,但对于由TypeLib接口或外部应用程序或ActiveX组件输入创建的对象则不都可行。你可以将第一种方法用于外部应用程序对象,而可以将第二种方法用于内部对象。第三种方法是为了R14的向后兼容。

提示! 当你选择对属性和方法用Get或Put中的任意一种形式时,你会发现用较长的形式(如vlax-put-property)比用较短的形式(如vla-put-color)更灵活。原因是通过从函数中分离出属性名称,你就可以定义函数和循环语句,它们可以接受一个属性列表及相关的数值,例如…

(defun MapPropertyList (object proplist)
  (foreach propset proplist
    (if (vlax-property-available-p object (car propset))
      (vlax-put-property object (car propset) (cadr propset))
    )
  )
)

当试图在方法上使用这种方式时要特别小心,因为方法的参数列表会随着对象和方法的变化而变化。有一些方法不需要参数,而另一些则随长度变化。

数据类型

数据类型是用于描述给定对象或属性可包含的数值的类型。数据类型的例子有整数、双精度、货币、日期、字符串等等。在AutoLISP已经享受“类型无关”多年后,Visual
LISP也同样可以,但不单只这样。在AutoCAD里,你可以尽可能地通过AutoLISP来保留这种类型无关,但当涉及其它应用程序时,例如Microsoft
Excel,你将不可避免而且明智地用到数据类型。

类型无关并非免费午餐,其代价是效率低下。当你提前声明了数据类型,你就通知编译器调动仅仅够用的资源来匹配预定数据。例如,存储整数类型的数据远比存储“长”数据值要低得多。当你未使用数据类型时,所有数据都会自动分配尽可能长的数据类型,以确保能匹配所有可能出现的数据。其结果导致应用程序消耗的资源远比它的需求大得多,无论是从初始加载的尺寸或者其运行时间资源的分配。

这就是为什么程序开发的语言中,象C++、Java,甚至是Visual
Basic通常都是比较快的(相比使用自由类型语言编程的同样功能)。他们确保能提前精简处理以保证运行时有更快的性能。AutoLISP并不这样做,因此是相对慢的处理语言。而Visual
LISP相对会快一些,但只有你利用其新的特性,让其最尽可能发挥。

常量和枚举
常量是特殊的数据类型。正如其名,它的值是不可改变的。有时被称为静态。通常来说,常量是由程序语言或由托管应用程序本身提供,作为一种方便的手段。例如,acByLayer常量在属性值中可以被256代替。名称值比整数值更好明白也更好记。例如,以下两个语句在功能上说是一致的。

(vla-put-color object acByLayer) 
(vla-put-color object 256) 

枚举是常量的逻辑组合,它用来确定常量值范围。例如你可以用颜色值1、2、3、4、5,但也可以将之常量化为acRed、acYellow、acGreen、acCyan、acBlue、acMagenta和acWhite,这样更清晰。这种类型的相关常量值范围即称为枚举。参看附录A为标准的AutoCAD枚举清单。

提示!并不是所有的ActiveX枚举都在Visual
LISP中有提供。例如,标准的Decimal(十进制)或Short(短)数据类型并没有镜像为vlax-vbDecimal或vlax-vbShort。请参阅第六章学习更多关于数据类型的信息。

变体和安全数组
在上节的数据类型中,有提到对自由类型声明使用最大的可用配额,例如在AutoLISP中的(setq)语句。事实上,是分配了一个变体数据类型,变体包括所有的数据类型,它提供了足够的资源空间去包含任何其它数据类型,可以是数值、日期、字符串或其它。事实上,变体数据类型是ActiveX产生出来的。但这个概念更具有一般性,在ActiveX之前已经存在很长的时间。

事实上Visual
LISP包含了将所有ActiveX数据转换为带有指示包含哪类类型的区分符的变体。感觉有点乱,但事实很简单。该容器是一个包含有货币数据类型值的变体,当你分配给对象一个新值时,你必须提供区分符以使得这个数据能够正确储存。这是必须要做的,尤其是你在AutoCAD和其它应用程序(如Microsoft
Excel)之间传递数值时。

除了发送数值外,你还可以从变体值中查询嵌套的数据类型,也可将值正确的转换成为相关的LISP数据类型。例如,你可查询变体对象值是否包含了双精度值。然后你可以在LISP中将这个数值读取为实数(REAL)数据类型。Visual
LISP提供了大量的函数用于在LISP环境中创建、读取、修改变体量数据值。

安全数组相当于AutoLISP中的LIST对象。主要的区别在于它是静态的。意思就是说他不可能根据他们能存储的数字的多少来加长或改变项数。这将避免了在试图分配或取得超出数组长度的元素时产生的不必要的错误。这就是它们之所以被称为“安全”的原因。任何传递到ActiveX的LIST结构必须先转换成安全数组。任何从ActiveX对象获得的LIST面向对象的数据都必须通过象(car)、(nth)、(accoc)、(mapcar)、(member)等的LISP函数转换成LIST数据类型。Visual
LISP为创造操作、阅读安全数组数据值提供了大量的功能。

更多关于变体和安全数组的内容,请参阅第六章。

命名空间
命名空间是分配用于运行过程的虚拟空间,它与空间中的其它资源相互配合。但它有时也可以和其它命名空间中的其它过程相联系。可以把命名空间想象成卧室。你的应用程序就如在卧室工作的人,也即在特定命名空间的过程。其它的应用程序就如在邻近卧室(命名空间)工作的一样,这两个人可以相互独立和隔离的,但他们也可以互相传递数据以便沟通。这就是命名空间工作的原理。

使用命名空间的好处在于可以在特定的命名空间中的过程与另外命名空间相独立的,这样可以在它们之间不相互破坏(如在争夺保存资源时)。它们也可直接通过它们的命名空间加载和卸载过程。换句话说,这有点象拆掉房子里一间卧室,而房子就如模块化形式建的一样。移去一个房间不会影响到其它房间或过程的生存。

可能使用命名空间最大的短处是会导致在操作系统或其宿主应用程序上的一些开销。为了管理好给定的命名空间,它必须有自己的内存地址范围和指针分配。这需要消耗额外的资源去跟踪和控制命名空间,这反过来又在必要时提供了直接的方法去访问它、卸载它或暂停它。

AutoCAD提供了在Visual
LISP内部对命名空间的内部管理方法,同样在ObjectARX和VBA中也可以。这是Visual
LISP比AutoLISP功能强大改进的又一表现。实际上每个打开的文档都是其自己的命名空间(如果你不是在单文件模式下工作的话),当在一个图形中设置一个变量而又试图在另一个图形中读取它时,这个影响就会看到。是有办法可以实现在这些图形中传递变量的,我们第十章将会谈到。

接口和类型库
接口是用于连接其它ActiveX过程或构件的对象模型的手段。当你想利用其它应用程序的特定的属性、常量或方法时,你首先要定义一个接口去加载那个目标应用的对象模型。例如,有时你想通过Visual
LISP来利用Excel自己的工具直接用Microsoft
Excel来保存一些AutoCAD信息到电子表格文件。这就需要定义接口,然后就可使用类型库

要使用类型库,就必须将其加载到内存,并确定接口指针已经定义。Visual
LISP提供了一毓函数用于加载和配置类型库接口。

(vlax-import-type-library 
  :tlb-filename name string 
  :methods-prefix    string 
  :properties-prefix string 
  :constants-prefix  string 

输入类型库参照到当前命名空间中。

参数: 
:tlb-filename string – (字符串) 为类型库文件的路径和文件名 
:methods-prefix string – (字符串) 为任意前缀字符串标识符 
:properties-prefix string – (字符串) 为任意前缀字符串标识符
:constants-prefix string – (字符串) 为任意前缀字符串标识符

示例: 
(vlax-import-type-library
  :tlb-filename             “c:\myfiles\typelibs\tlfile.tlb”
  :methods-prefix           “dsxm-“
  :properties-prefix        “dsxp-“
  :constants-prefix         “dsxc-“
)

该示例输入了由tlfile.tlb文件定义的外部应用程序或控件的类型库接口。其它的参数定义了类型库接口所暴露的方法、属性和常量的前缀。

如果该类型库提供了一个名为AddNumbers的方法,在我们的Visual
LISP代码中它将被识别为dsxm-AddNumbers。有趣的是,一旦你已经输入了类型库并且程序运行成功后,Visual
LISP将识别这些外部应用程序中的所有已定义的属性、方法、常量,并将其做为内置的LISP函数把它们的代码变蓝。这也是Visual
LISP IDE可以帮你编程及提高你及时发现错误的能力的另一原因。

图3-3 类型库接口

类型库仅仅是一个接口,它暴露了提供者的所有对象模型成员供给其它应用程序查询用。当加载类型库时,它立刻定义并识别相关应用程序提供者的所有公开暴露的属性、常量和方法给应用程序使用者使用。

在图3-3中,Excel类型库被加载以做便Visual
LISP可以连接到Excel对象模型并使用其暴露的工具。这样可以节省你很多时间和麻烦,因为它在Excel中建立了直接的访问工具,这样就可以做你想需要的而不必尝试在Visual
LISP中重复另起炉灶。下面的例子显示了它是如何使用的。

例如,当通过从Visual
LISP中调用,将一个定值做为参数提供给Excel函数,你可以用常量枚举名来代替实际所代表的数值以使得你的编码清晰易懂。这也省去了你需要浏览Excel中的所有枚举并把它们翻译成Visual
LISP的时间。如果Excel提供了一个如put-cellcolor的常量时,那就可以在Visual
LISP中直接使用它。

Visual
LISP需要类型库信息来确定对象的方法、属性和常量是否存在。有些对象不含有任何类型库信息,例如AcadDocument对象。

(vlax-typeinfo-available-p object) 

如果对象的类型库信息存在,返回T。如果不存在,则返回nil。

参数:
Object – 一个vla-对象。 

(defun Excel-Get-Cell (rng row column)
  (vlax-variant-value
    (msxl-get-item
      (msxl-get-cells rng)
      (vlax-make-variant row)
      (vlax-make-variant column)
    )
  )
)
(defun Excel-Put-CellColor (row col intcol / rng)
  (setq rng (Excel-Get-Cell (msxl-get-ActiveSheet xlapp) row col))
  (msxl-put-colorindex (msxl-get-interior rng) intcol)
)

上例中定义的第二个函数提供了一个在Visual
LISP中对一个Excel工作表中的给定单元填色的方法。这可以通过使用Excel中已暴露接口的方法,这是通过首先加载Excel类型库实现的。这个以上显示的类型库项带有一个 msxl-前缀。

当你调用了一个类型库接口,其中涉及到的函数随后会被VLISP编辑器语法引擎识别到。当你正确地输入了它们后,它们就会改变颜色以显示它们确实被从外面的类型库接口中被视为一个有效的函数而显示出来。这是一个很有用的编码练习的基础:语法感知。

提示!类型库有很多种形式,他们通常是.TLB文件。但也可以是.OLB、.DLL,甚至是.EXE。值得一提的是Microsoft
Office 97 和 2000版通常用.TLB文件,而OFFICE
XP自己使用.EXE文件来对类型库提供对其它应用的接口定义。请翻阅与你准备使用外部应用程序或服务相关的参考资料以了解它是如何暴露共ActiveX类型库信息的内容。

尽管变体和安全数组数据类型这个话题在本书的前几节已经讲过,但在Visual
LISP的ActiveX领域中它们具有相当重要的位置,值得我们用一整章的篇幅来专门讲解。我们将首先简要了解他们是什么,然后开始探索如何通过Visual
LISP函数来使用它们。

正如我们前面提到的,变体是一种数据类型,它被设计成能包含所有任何其它数据的通用容器。他们消耗最多的内存并可处理所有的数据类型,因为他们会根据资源需求取最大内存。

象C/C++、Visual
Basic以及Delphi这类语言会提供声明语句去提前通知编译器确定每个变量将包含什么数据类型。这不仅保证了精简的资源需求,同时也允许编辑期间进行错误检查,以防止运行时的问题。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图