惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
L
LangChain Blog
IT之家
IT之家
S
SegmentFault 最新的问题
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
The Blog of Author Tim Ferriss
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
U
Unit 42
B
Blog RSS Feed
GbyAI
GbyAI
Microsoft Security Blog
Microsoft Security Blog
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
Vercel News
Vercel News
S
Schneier on Security
Spread Privacy
Spread Privacy
C
Cyber Attacks, Cyber Crime and Cyber Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
雷峰网
雷峰网
博客园_首页
人人都是产品经理
人人都是产品经理
P
Palo Alto Networks Blog
The Hacker News
The Hacker News
T
Tor Project blog
L
Lohrmann on Cybersecurity
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy International News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tenable Blog
V
Vulnerabilities – Threatpost
大猫的无限游戏
大猫的无限游戏
博客园 - 【当耐特】
V
V2EX
Security Latest
Security Latest
A
About on SuperTechFans
Cloudbric
Cloudbric
S
Security Affairs
MongoDB | Blog
MongoDB | Blog
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
TaoSecurity Blog
TaoSecurity Blog

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 总结一些iOS项目中组织代码的方法 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放 Effective-Objective-C-读书笔记-Item-4-如何正确定义常量 UITextView编辑时插入自定义表情-简单的图文混编 关于评论不见了=。= Entity和Model的不同-关于代码的数据层 一次审核被拒的经历-关于iCloud到底应该备份什么数据 Block类型变量-缓存Http请求与回调 提升UITableView性能-复杂页面的优化 NSString的Copy与内存分配 利用NSProxy实现消息转发-模块化的网络接口层设计-原创 Effective-Objective-C-读书笔记-Item-3 Effective-Objective-C-读书笔记-Item-2 Effective-Objective-C-读书笔记-Item-1 iOS项目的目录结构-原创 Android开源库-LinkTextView-原创 第一篇Blog
有关宏定义的经验与技巧-简化代码-增强Log
土土哥 · 2015-03-15 · via 土土哥的Blog

前言

宏定义、#define啥的,我们经常遇到。
一般来说,最常用的可能就是定义一些常量、简单的“函数”,如下:

1
2
3
4
5
//定义常量PI


//定义“函数”MIN

但是,这样定义常量、函数,有一定的风险。(见:Effective-Objective-C-读书笔记-Item-4-如何正确定义常量, 宏定义的黑魔法 - 宏菜鸟起飞手册

本文就列出几条我个人在iOS开发当中常用的经验与技巧。

简化代码

在开发当中,我们可能会大量的写一些重复的,甚至具有“危险”的代码,用宏定义#define,往往可以简化代码,看看下面几个例子。

GCD与#define

dispatch_once
项目中往往少不了各种单例类,我们一般会如下定义:

1
2
3
4
5
6
7
8
9
10
+ (instancetype)sharedService {
static XXX *sharedService = nil

static dispatch_once_t onceToken
dispatch_once, ^{
sharedService = [XXX new]
})

return sharedService;
}

这时候,就要注意几点:

  • dispatch_once_t必须是static的,否则会各种报错=。=。
  • dispatch_once的第一个参数必须取onceToken的地址,就是要“&onceToken。”

看,写个小小的单例都要这么注意,错了一点都很危险啊,而且总要重复写这几行。所以这个时候需要让宏定义帮忙:

1
2
3
4
5
6
7
8
9
10
11
12
13

#define DISPATCH_ONCE_BLOCK(onceBlock) static dispatch_once_t onceToken; dispatch_once(&onceToken, onceBlock);

+ (instancetype)sharedService {
static XXX *sharedService = nil;


DISPATCH_ONCE_BLOCK(^{
sharedService = [XXX new];
})

return sharedService;
}

看,是不是变得更加简洁、“安全了”~

dispatch_async
除了dispatch_once,另一个常用的就是dispatch_async。不多说,直接看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#define DISPATCH_ON_MAIN_THREAD(mainQueueBlock) dispatch_async(dispatch_get_main_queue(), mainQueueBlock);


#define DISPATCH_ON_GLOBAL_QUEUE_HIGH(globalQueueBlocl) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), globalQueueBlocl);
#define DISPATCH_ON_GLOBAL_QUEUE_DEFAULT(globalQueueBlocl) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), globalQueueBlocl);
#define DISPATCH_ON_GLOBAL_QUEUE_LOW(globalQueueBlocl) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), globalQueueBlocl);
#define DISPATCH_ON_GLOBAL_QUEUE_BACKGROUND(globalQueueBlocl) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), globalQueueBlocl);


DISPATCH_ON_MAIN_THREAD(^{

})


DISPATCH_ON_GLOBAL_QUEUE_DEFAULT(^{

})

再也不用写一大堆”dispatch_async(dispatch_get_global_queue”了~看起来是不是很清爽~

能一行搞定,干嘛还要写多行

很多时候,我们写的函数、方法都会在起始的地方加一些公共的判断,条件满足了才可以继续往下运行,如“用户是否登录”、“某个模块是否加载完成”等等,所以,我们可能会写如下样子的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)doSomething {
if (![[XXXAccountService sharedService] isLogin]) {
return;
}

}

- (void)doSomething2 {
if (![[XXXAccountService sharedService] isLogin]) {
return;
}

}

每次都要这么写,是不是感觉很浪费时间?而且重复的好多=。=
所以,我们要用#define,把3行变成一行:

1
2
3
4
5
6
7
8
9
10
11
12

#define CHECK_LOGIN_IN if (![[XXXAccountService sharedService] isLogin]) {return;}

- (void)doSomething {
CHECK_LOGIN_IN

}

- (void)doSomething2 {
CHECK_LOGIN_IN

}

其实#define的原理就是不管三七二十一,直接做替换,所以我们完全可以利用这个特点,发挥自己的想象,简化代码~

宏定义与Log

打Log,是个永恒的话题。

NSLog增强版

我们会经常用到NSLog打Log,但是,这个函数能提供的信息非常少,所以,下面就是一个增强版, 最好定义在项目的pch文件中:

1
2
3
4
5
6
7
8

do { \
NSLog(@"<%@ : %d : %s>-: %@", \
[[NSString stringWithUTF8String:__FILE__] lastPathComponent], \
__LINE__, \
__FUNCTION__, \
[NSString stringWithFormat:format,
} while(0)

使用:

1
2
3
4
5
6
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSLog(@"Blog: %@", @"http://tutuge.me");
}
return 0;
}

输出:

1
<main.m : 22 : main>-: Blog: http:

看,这下Log就带上了文件名、行号、函数名,一下子就清楚多了~
至于为什么要用这个奇怪的“do{…}while(0)”,其实就是为了防止在不同的使用场景中导致语法错误,详细请见:宏定义的黑魔法 - 宏菜鸟起飞手册

为Log增加全局开关

开发中,我们往往会在许多地方加上Log,但是在发布的时候,又不想显示这些Log,这个时候难道要一个一个的删除打Log的地方?
所以,为了增加开关,我们需要将上面的增强型NSLog改造一下:

1
2
3
4
5
6
7

#ifdef NEED_DEBUG
#define NSLog(format, ...) \

#else
#define NSLog(format, ...) do{ } while(0)
#endif

所以,当我们需要Log的时候,在上面定义的之前加上下面这一行:

1
#define NEED_DEBUG

就可以打开Log了。不加的话,就不会有Log~简单的条件编译~

为Log增加等级

开发过Android的应该都知道,Android里面可以很方便的用Log.i、Log.d输出不同“等级”的Log,调试更加方便,NSLog却不行。所以,还是要借助宏定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#ifdef LOG_LEVEL_DEBUG
#define DLog(format, ...) NSLog(@"<DEBUG>: %@", [NSString stringWithFormat:format, ##__VA_ARGS__])
#else
#define DLog(format, ...) do{ } while(0)
#endif


#ifdef LOG_LEVEL_INFO
#define ILog(format, ...) NSLog(@"<Info>: %@", [NSString stringWithFormat:format, ##__VA_ARGS__])
#else
#define ILog(format, ...) do{ } while(0)
#endif


#ifdef LOG_LEVEL_ERROR
#define ELog(format, ...) NSLog(@"<Error>: %@", [NSString stringWithFormat:format, ##__VA_ARGS__])
#else
#define ELog(format, ...) do{ } while(0)
#endif

当我们在打Log的时候,就可以根据需要,用不同等级的Log,而且可以方便的定制需要输出哪几种,并且之前的“增强版”NSLog也可以使用。

完整的请看Gist.

其它

还有一些简单好用的宏定义,在这就只举几个典型的例子了~

打印自定义类型信息

假如有如下结构体定义(自定义类的话,直接重写description方法即可):

1
2
3
4
5
6
7
8
9

typedef struct Complex {
float r;
float i;
} Complex;



#define NSLogComplex(complex) NSLog(@"Complex: (%g%s%gi)", complex.r, (complex.i > 0 ? "+" : ""), complex.i)

常用函数简化、单例获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#define GetViewWidth(view) view.frame.size.width
#define GetViewHeight(view) view.frame.size.height
#define GetViewX(view) view.frame.origin.x
#define GetViewY(view) view.frame.origin.y


#define GetScreenWidth [[UIScreen mainScreen] bounds].size.width
#define GetScreenHeight [[UIScreen mainScreen] bounds].size.height


#define GetImage(imageName) [UIImage imageNamed:[NSString stringWithFormat:@"%@",imageName]]


总结

宏定义的各种Tip还远远不止这些,合理使用它,可以有效改善代码的阅读质量~

参考