小小的宏 大大的世界

321次阅读  |  发布于2年以前

小小的宏,大大的世界

题记:用最通俗的语言,描述最难懂的技术

目录表

关于UIKITEXTERN

在开发中大家难免会声明很多的全局常量,以确保在整个项目中可用,在这里这个全局常量的值一般不会变,且全局有效,在iOS中的表现方式就是这样的,这段代码既保证了全局可用,又防止了不同动态库之间的引用冲突

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

同样也被很多优秀的框架引用,比如YYKit

1.1 eg
#ifdef __cplusplus
#define YY_EXTERN_C_BEGIN  extern "C" {
#define YY_EXTERN_C_END  }
#else
#define YY_EXTERN_C_BEGIN
#define YY_EXTERN_C_END
#endi

那么为什么这么写?这么写会有什么作用?适用于那种场景?以及它的原理,接下来我们一步一步揭开神秘的面纱,Let's go.. UIKITEXTERN代码解读

如果是C++语言
那么就声明此宏定义,在C++语言里面写C需要加上C的说明,设置编译器属性为default,保证对外部的类可见
否则
就声明此宏定义,设置编译器属性为default,保证对外部的类可见

在这里,宏仅仅是对象宏,等量替换,后者替换前者

原理

__attribute__格式

__attribute__ ((attribute-list)) 
这两个括号是一定要带上的,千万记得,千万记得,千万记得
 test2.cc 可以调用func1,原因是test1.o和test2.o同属于一个so文件(so文件是Linux下的动态库文件,等同于iOS下的动态库UIKit等)

// test1.cc file
# include <stdio.h>
extern "C" void func1();
{
printf("in %s\n,__FUNCTION__);
}

// 如果编译的时候设置了参数-fvisibility=hidden,下面可以省略设置
// compile
__attribute__ ((visibility("hidden"))) void func1();


// test2.cc file
# include <stdio.h>
extern "C" void func1();
extern "C" void func2()
{
func1();
printf("in %s\n,__FUNCTION__);
}

// compile
__attribute__ ((visibility("default"))) void func2();


// main.cc file
extern "C" void func1();
extern "C" void func2();
int main ()
{
func1();
func2();
return 0;
}


// compile result
ll:test
test:main.o libvisibility.so
        g++ -o test main.o -lvisibility -L .
main.o::main.cc
        g++ -c main.cc
libvisibility.so:test1.o test2.o
        g++ -shared -o libvisibility.so test1.o test2.o
test1.o:test1.cc
        g++ -fvisibility=hidden -fPIC -c test1.cc
test2.o:test2.cc
        g++ -fvisibility=hidden -fPIC -c test2.cc
clean:
        rm -f *.o *.so test
        $ make
g++ -c main.cc
g++ -fvisibility=hidden -fPIC -c test1.cc
g++ -fvisibility=hidden -fPIC -c test2.cc
g++ -shared -o libvisibility.so test1.o test2.o
g++ -o test main.o -lvisibility -L .
main.o: In function `main':
main.cc:(.text+0x5): undefined reference to `fun1'
collect2: ld returned 1 exit status
make: *** [test] Error 1

结论:main()中不可以调用func1,可以调用func2
原因:func1设置了对外不可见,func2设置对外部可见
查看:使用readelf对各个.o文件(可执行文件)分析

$ readelf -s test1.o | grep fun
     6: 0000000000000007     5 OBJECT  LOCAL  DEFAULT    6 _ZZ4fun1E12__FUNCTION__
    12: 0000000000000000    30 FUNC    GLOBAL HIDDEN    2 func1
$ readelf -s test2.o | grep fun
     6: 0000000000000007     5 OBJECT  LOCAL  DEFAULT    6 _ZZ4fun2E12__FUNCTION__
    12: 0000000000000000    35 FUNC    GLOBAL DEFAULT    2 func2
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND fun1
$ readelf -s libvisibility.so | grep fun
     9: 00000000000006ac    35 FUNC    GLOBAL DEFAULT   12 func2
    41: 000000000000071d     5 OBJECT  LOCAL  DEFAULT   14 _ZZ4fun1E12__FUNCTION__
    43: 0000000000000729     5 OBJECT  LOCAL  DEFAULT   14 _ZZ4fun2E12__FUNCTION__
    48: 000000000000068c    30 FUNC    LOCAL  HIDDEN   12 func1
    54: 00000000000006ac    35 FUNC    GLOBAL DEFAULT   12 func2

结果:func1的visibility属性是HIDDEN,func2的visibility的属性DEFAULT

应用场景

扩展

这个题是喵神文章的题目,仅供参考
// concat two params
#define __SN_PASTE__(A,B) A##B

// main macro
#define SNSquare(A) __SNSQUARE_IMPL__(A,__COUNTER__)

// param counter
#define __SNSQUARE_IMPL__(A,L) ({ __typeof__(A) __SN_PASTE__(__a,L) = (A); \
                                (__SN_PASTE__(__a,L)) * (__SN_PASTE__(__a,L)); }) \


// eg
int res =  SNSquare(3);
NSLog(@"3 square is %d",res);

❝关于宏,喵神写了一篇非常好的文章,这里就不再赘述,链接见文章底部

extern

 // 在"ViewController.h" 中进行定义全局变量
#import "ViewController.h"
int x = 24;

@interface ViewController ()

@end

@implementation TestViewController

- (void)viewDidLoad {
      [super viewDidLoad];

      self.view.backgroundColor = [UIColor purpleColor];;
}
@end


// 在BViewController.m中进行声明全局变量

#import "BViewController.h"
@interface BViewController ()

@end

@implementation BViewController

extern int x;
- (void)viewDidLoad {
      [super viewDidLoad];

      self.view.backgroundColor = [UIColor whiteColor];
      NSLog(@"x is:%d",x);// x is 24
}
@end

static

 static int a = 3;
++a;
NSLog(@"a is %d",a); // a is 4

局部变量会被延长生命周期,不再跟大括号有关系,直到程序结束才会被销毁,下面的例子除非程序终止,否则a一直有值- 内存只存在一份

 static int a = 3;
++a;
NSLog(@"a is %d and addr is %p",a,&a); // a is 4


// printf result
a is 4 and addr is 0x106012954
a is 5 and addr is 0x106012954
a is 6 and addr is 0x106012954
a is 7 and addr is 0x106012954
  #import "ViewController.h"

static int a = 24;

@interface ViewController ()

@end

@implementation TestViewController

- (void)viewDidLoad {
     [super viewDidLoad];

 // a is visibility in ViewController.m
}
@end

const

const修饰基本变量

注释:const修饰右侧的变量

Line:150 152 154是等价的,修饰的都是*q
Line:153 修饰的是q
// in ViewController.m
NSString * const kTableViewCell = @"kTableViewCell";
static NSString * const kTableViewCell = @"kTableViewCell";

❝结论:使用static和const修饰的变量的作用域是在当前文件,且不会被更改

// in BViewController.h file
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

// Read news event
extern NSString * const SNNewsReadNewsEvent;

@interface BViewController : UIViewController

@end

NS_ASSUME_NONNULL_END

// in BViewController.m file
#import "BViewController.h"
NSString * const SNNewsReadNewsEvent = @"SNNewsReadNewsEvent";
@interface BViewController ()

@end

@implementation BViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

iOS下的extern

UIKIT框架处理,通过文章开头的例子概括就是将函数修饰以兼容C++编译方式的,具有extern属性的(对外部文件可见)一个宏字段

UIKIT_EXTERN是在UIKit框架里面UIKitDefines.h中定义的- FOUNDATION_EXTERN

 // in BViewController.h file
 #import <UIKit/UIKit.h>

 NS_ASSUME_NONNULL_BEGIN

 // UIKIT_EXTERN eg
 UIKIT_EXTERN const CGFloat kAnimationInterval;

 // FOUNDATION_EXTERN eg
 FOUNDATION_EXTERN NSString * const kAnimationKey;

 @interface BViewController : UIViewController

 @end

 NS_ASSUME_NONNULL_END


 // in BViewController.m file
 #import "BViewController.h"

 const CGFloat kAnimationInterval = 0.5f;
 NSString * const kAnimationKey = @"kAnimationKey";

 @interface BViewController ()

 @end

 @implementation BViewController


 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
 }

 @end

参考文档

结束语

关于一个小小的宏字段,引发了很多的畅想,还有一些更高级的用法,感兴趣的作者可以搜索下ReactiveCocoa这个框架,作者把宏应用到出神入化的地步,简直出天际!!!技术之路需要不断输入输出,我们都是平凡人,只不过看谁坚持的久而已。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8