logo头像

Renbo’s Blog

iOS中常见的宏

自定义宏

  • 处理NSLog事件(开发者模式打印,发布者模式不打印)
#ifdef DEBUG
  #define NSLog(FORMAT, ...) fprintf(stderr, "[%s(%d):%s]\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
  #define NSLog(FORMAT, ...) nil
#endif
  • 只在OC语言的情况下引用
// 保证 #ifdef 中的内容只会在 ObjectivObjective-C,在 C/C++ 代码中不会被引用
#ifdef __OBJC__
   // 定义宏或导入头文件
#endif
  • 判断当前的iPhone设备/系统版本
//判断是否为iPhone
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
//判断是否为iPad
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
//判断是否为ipod
#define IS_IPOD ([[[UIDevice currentDevice] model] isEqualToString:@"iPod touch"])
//获取系统版本
#define IOS_SYSTEM_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
  • 使用 ARC 和 MRC
#if __has_feature(objc_arc)
    // ARC
#else
    // MRC
#endif
  • 宏与const 的使用
宏的用法:一般定义代码就用宏。
const用法:一般定义一个常量字符串就用const(如,服务器的地址)。
static NSString * const server_host = @"api.apple.com";
  • 判断当前设备是模拟器还是真机
#if TARGET_IPHONE_SIMULATOR  
    // 模拟器  
#elif TARGET_OS_IPHONE  
    // 真机  
#endif  
  • 判断当前设备系统
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0  
    // 这里写设备系统大于8.0 以上的代码  
#else  
    // 这里写设备系统小于8.0以上的代码  
#endif 


#if __IPHONE_OS_VERSION_MIN_REQUIRED <= __IPHONE_7_0  
    // 这里写设备系统小于7.0以上的代码  
#else  
    // 这里写设备系统大于7.0以上的代码  
#endif 
  • 设置随机颜色
#define GRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
  • 静态库(.a文件)中有分类,调用分类中的方法 crash
/**
    静态库中编写 Category 时的便利宏,用于解决 Category 方法从静态库中加载需要特别设置的问题。
    加入这个宏后,不需要再在 Xcode 的 Other Liker Fliags 中设置链接库参数(-Objc / -all_load / -force_load)
    *******************************************************************************
    使用:在静态库中每个分类的 @implementation 前添加这个宏
    Example:
        #import "NSString+BRAdd.h"

        BRSYNTH_DUMMY_CLASS(NSString_BRAdd)
        @implementation NSString (BRAdd)
        @end
 */
#ifndef BRSYNTH_DUMMY_CLASS

    #define BRSYNTH_DUMMY_CLASS(_name_) \
    @interface BRSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
    @implementation BRSYNTH_DUMMY_CLASS_ ## _name_ @end

#endif

// 在ios开发过程中,有时候会用到第三方的静态库(.a文件),OC没有为每个函数(或者方法)定义链接符号,它只为每个类创建链接符号。这样当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来,就会导致你调用类别中的方法时,会出现selector not recognized的错误,从而导致app闪退。使用这段宏定义他可以虚拟新建一个与名字category 相同.h.m 让编译器 编译通过。即可解决上面的问题。
  • 合成弱引用或者强引用。
/**
 Synthesize a weak or strong reference.

 Example:
    @weakify(self)
    [self doSomething^{
        @strongify(self)
        if (!self) return;
        ...
    }];

 */

#ifndef weakify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
        #endif
    #endif
#endif

#ifndef strongify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
        #endif
    #endif
#endif
  • 断言
// 1.NSAssert / NSCAssert 的用法(如果条件为假,就会抛出异常)
// 前者适用于Objective-C的方法,后者适用于C的函数。
int a = 4;  
NSAssert(a == 5, @"a must equal to 5"); //第一个参数是条件,如果条件为假,就会抛出异常并打印第二个参数的内容
// 注意:NSAssert 的定义中有self(即持有self的strong引用),在block中使用时要避免出现循环引用问题。NSCAssert 的用法与 NSAssert 一致,适用于C语言的函数,在OC中也可以使用,并且没有持有self,在block中使用不会出现循环引用问题

// 2.NSParameterAssert / NSCparameterAssert 的用法(是针对参数是否存在的断言)
// 前者适用于Objective-C的方法,后者适用于C的函数。
- (void)assertWithPara:(NSString *)str {  
    NSParameterAssert(str); //只需要一个参数,如果参数存在程序继续运行,如果参数为空,则程序停止并打印日志  
    //further code ...  
}

// Xcode 已经默认将release环境下的断言取消了, 免除了忘记关闭断言造成的程序不稳定。

// 常用的几个自定义断言
#define BRAssertNil(condition, description, ...) NSAssert(!(condition), (description), ##__VA_ARGS__)
#define BRCAssertNil(condition, description, ...) NSCAssert(!(condition), (description), ##__VA_ARGS__)

#define BRAssertNotNil(condition, description, ...) NSAssert((condition), (description), ##__VA_ARGS__)
#define BRCAssertNotNil(condition, description, ...) NSCAssert((condition), (description), ##__VA_ARGS__)

#define BRAssertMainThread() NSAssert([NSThread isMainThread], @"This method must be called on the main thread")
#define BRCAssertMainThread() NSCAssert([NSThread isMainThread], @"This method must be called on the main thread")

系统宏

  • NS_AVAILABLE_IOS 与 NS_DEPRECATED_IOS
// 在iOS系统的API中,我们经常会看到下面的一些宏
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullable NSDictionary<NSAttributedStringKey, id> *)attributes context:(nullable NSStringDrawingContext *)context NS_AVAILABLE(10_11, 7_0);

- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode NS_DEPRECATED_IOS(2_0, 7_0, "Use -boundingRectWithSize:options:attributes:context:") __TVOS_PROHIBITED;

/**
    含义:(AVAILABLE:可用; DEPRECATED:弃用)
    NS_AVAILABLE(10_11, 7_0) 表示自 Mac10.11 和 iOS7.0 引入该函数,即 iOS7之后 使用这个函数
    NS_AVAILABEL_IOS(6_0) 表示自 iOS6.0 引入该方法,若在iOS6.0之前的版本使用该函数,则会导致 Crash;
    NS_DEPRECATED_IOS(2_0, 7_0, "替代函数")  表示该函数自 iOS2.0 引入,在 iOS7.0 被废弃。推荐 iOS7.0 之后使用替代函数。
    NS_DEPRECATED(10_6, 10_9, 2_0, 7_0) 表示自 Mac10.6 和 iOS2.0 引入,在 Mac10.9 和 iOS7.0 被废弃
 */
  • 忽略编译器警告宏
// 处理编译器警告(下面 #pragma 的作用是,去掉方法的警告提示)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

CGSize size = [self sizeWithFont:font constrainedToSize:maxSize lineBreakMode:lineBreakMode];

#pragma clang diagnostic pop
  • 全局设置 nonnull 类型的宏
NS_ASSUME_NONNULL_BEGIN

/**
 Provides extensions for `UIBarButtonItem`.
 */
@interface UIBarButtonItem (YYAdd)

/**
 The block that invoked when the item is selected. The objects captured by block
 will retained by the ButtonItem.

 @discussion This param is conflict with `target` and `action` property.
 Set this will set `target` and `action` property to some internal objects.
 */
@property (nullable, nonatomic, copy) void (^actionBlock)(id);

@end

NS_ASSUME_NONNULL_END

NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 两个宏之间的代码,所有指针对象都被假定为nonnull, 即不能为空,否则编辑器会报警告 Null passed to a callee that requires a non-null argument

该段代码使用了 NS_ASSUME_NONNULL_BEGIN , NS_ASSUME_NONNULL_END

两个宏中间包含的属性,参数值,返回值,默认是 nonnull 类型。

如果想要某个属性,参数值或者返回值为可选类型,则单独在该属性,参数值,或者返回值前单独标明nullable

上一篇