有关宏定义的经验与技巧-简化代码-增强Log

转自http://tutuge.me/2015/03/15/有关宏定义的经验与技巧-简化代码-增强Log/



有关宏定义的经验与技巧-简化代码-增强Log

前言

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

//定义常量PI
#define PI 3.1415926

//定义“函数”MIN
#define MIN(A,B) ((A) < (B) ? (A) : (B))

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

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

简化代码

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

GCD与#define

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

+ (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。”

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

//定义宏定义
#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。不多说,直接看例子:

//在Main线程上运行
#define DISPATCH_ON_MAIN_THREAD(mainQueueBlock) dispatch_async(dispatch_get_main_queue(), mainQueueBlock);

//在Global Queue上运行
#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(^{
    //更新UI
})

//Global Queue 
DISPATCH_ON_GLOBAL_QUEUE_DEFAULT(^{
    //异步耗时任务
})

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

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

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

- (void)doSomething {
    if (![[XXXAccountService sharedService] isLogin]) {
        return;
    }
    //do staff...
}

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

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

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

- (void)doSomething {
    CHECK_LOGIN_IN
    //do staff...
}

- (void)doSomething2 {
    CHECK_LOGIN_IN
    //do staff...
}

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

宏定义与Log

打Log,是个永恒的话题。 

NSLog增强版

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

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

使用: 

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

输出:

<main.m : 22 : main>-: Blog: http://tutuge.me

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

为Log增加全局开关

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

//判断是否要Log
#ifdef NEED_DEBUG    
#define NSLog(format, ...) \
    //Log定义...
#else
    #define NSLog(format, ...) do{ } while(0)
#endif

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

#define NEED_DEBUG

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

为Log增加等级

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

//Debug等级Log, 在此之前定义自己的NSLog
#ifdef LOG_LEVEL_DEBUG
    #define DLog(format, ...) NSLog(@"<DEBUG>: %@", [NSString stringWithFormat:format, ##__VA_ARGS__])
#else
    #define DLog(format, ...) do{ } while(0)
#endif

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

//Error等级Log
#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方法即可):

//复数结构体
typedef struct Complex {
    float r; //实部
    float i; //虚部
} Complex;

//定义打印Complex类型的宏
//输出:(1+2i), (1-2i)
#define NSLogComplex(complex) NSLog(@"Complex: (%g%s%gi)", complex.r, (complex.i > 0 ? "+" : ""), complex.i)

常用函数简化、单例获取

//获取View的属性
#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]]

//等等~只要你想得到~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值