美文网首页iOS Developer
Effective Objective-C 2.0阅读笔记与实践

Effective Objective-C 2.0阅读笔记与实践

作者: 托尼的夏天 | 来源:发表于2016-06-27 21:44 被阅读148次

    本文要点:

    • 多用类型常量,少用#define预处理指令
    • 用枚举表示状态、选项、状态码

    多用类型常量,少用#define预处理指令

    使用#define的弊端

    场景:编写代码时候我们经常要定义常量,例如我们在编写一段动画的逻辑,我们把一个动画的时常提取为常量:

    #define ANIMAITON_DURATION 0.3
    

    上面的编写姿势是不规范的,Why? 如下:
    #define预处理指令定义常量有两个弊端:

    • #define定义的常量没有类型信息 上述代码我们采用#define预处理指令,会把源码中ANIMAITON_DURATION字符串全部替换为 0.3,不过我们却无法获知 0.3的类型信息,这会使我们开发中产生迷惑
    • #define定义的常量可能会在无意中被人修改了,如果有人重新定义了常量,原有的值会被覆盖
    #define ANIMAITON_DURATION 0.3
    
    @implementation ViewController
    - (void)viewDidLoad {
       [super viewDidLoad];
    // 重新定义了 ANIMAITON_DURATION
    #define ANIMAITON_DURATION 0.4
       float value = 1.0 + ANIMAITON_DURATION;
       NSLog(@"%f",value);
    }
    

    如上输出结果是 1.4,原有的值0.3被覆盖了

    定义常量正确的姿势

    定义常量的正确的姿势: 使用类型常量

    static const NSTimeInterval KAnimationDuration 0.3
    

    上述定义的类型常量包含了类型信息,清楚的描述了常量的定义
    具体使用:

    • 私有常量

    如果不打算公开某个常量,比如该变量的使用,应把常量定义在常量的编译单元(tanslation unit,也就是.m文件)内,并建议在前面加上k字母;
    变量同时用 static 和const 修饰,const表示该变量的值不可修改,static修饰表示该变量仅在定义该变量的编译单元内可见,

    例子:MBProgressHUD
    MBProgressHUD.m定义了关于间距,字体大小的私有常量

    static const CGFloat kPadding = 4.f;
    static const CGFloat kLabelFontSize = 16.f;
    static const CGFloat kDetailsLabelFontSize = 12.f;
    
    • 公开常量

      有时候我们需要对外公开某个常量,比方说,你可能在类代码中调用了NSNotificationCenter派发通知,派发通知的时候,需要使用字符串常量标识通知的名称,而这个名称需要声明为一个外界可见的常量变量,这样注册者可以方便的调用;
      公开常量在头文件中用extern声明,并在相关实现文件中定义其值;
      公开常量放在全局符号表中(global symbol table),命名时候通常以与之相关的类名作为前缀,以避免名称冲突

    例子: SVProgressHUD
    SVProgressHUD.m文件中定义了与SVProgressHUD状态相关的几个通知

    NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
    NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification";
    NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification";
    NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification"
    

    SVProgressHUD.h文件对外公开了这些常量

    extern NSString * const SVProgressHUDDidReceiveTouchEventNotification;
    extern NSString * const SVProgressHUDDidTouchDownInsideNotification;
    extern NSString * const SVProgressHUDWillDisappearNotification;
    extern NSString * const SVProgressHUDDidDisappearNotification;
    extern NSString * const SVProgressHUDWillAppearNotification;
    extern NSString * const SVProgressHUDDidAppearNotification;
    

    注意const修饰的字符在常量类型的位置,常量定义应该从右往左边读,所以上述SVProgressHUDDidReceiveTouchEventNotification 是一个常量,而这个常量是一个指针,指向NSString对象,我们需要用const锁定这个指针

    用枚举表示状态、选项、状态码

    C语言枚举语法回顾
    enum EOCConnectionState {
     EOCConnectionStateDisconnected,
     EOCConnectionStateConnecting,
     EOCConnectionStateDisconnected,
    }
    

    定义枚举变量的方式:

    enum EOCConnectionState state = EOCConnectionStateDisconnected;
    

    上述的语法不太简洁,我们可以使用typedef重新定义枚举类型

    typedef enum EOCConnectionState = EOCConnectionState
     // 使用EOCConnectionState代替enum EOCConnectionState 
    EOCConnectionState state = EOCConnectionStateDisconnected
    

    我们还可以指定枚举使用何种底层数据类型如下,并且手工指定枚举成员的值

    enum EOCConnectionState:NSUInteger {
     EOCConnectionStateDisconnected = 1,
     EOCConnectionStateConnecting,
     EOCConnectionStateDisconnected,
    }
    

    如上指定了枚举的底层数据类型是NSUInteger,而且手动指定了枚举成员初始值为1
    如果枚举成员的值可以组合,我们一般用位移来表示,如下

    enum EOCConnectionState:NSUInteger {
     EOCPerimttedDirectionUP = 1<<0, 
     EOCPerimttedDirectionDown = 1<<1, 
     EOCPerimttedDirectionLeft = 1<<2,
     EOCPerimttedDirectionRight= 1<<3,
    }
    1 << 0 二进制 : 00000001   十进制 1
    1 << 1 二进制 : 00000010   十进制 2
    1 << 2 二进制 : 00001000   十进制 4
    1 << 3 二进制 : 00010000   十进制 8
    

    使用位移指定枚举的值得好处是我们可以方便使用位操作符 | 和 &操作 枚举成员,
    |操作:如果我们要组合两个枚举成员,我们可以用 |操作:

    enum EOCConnectionState statues = EOCConnectionStateDisconnected | EOCConnectionStateConnecting 
    

    上述代码的执行过程:

    二进制       十进制
    00000001      1         EOCConnectionStateDisconnected
    00000010      2          EOCConnectionStateConnecting
    00000011      3          EOCConnect ionStateDisconnected|EOCConnectionStateConnecting
    

    &操作:使用 &位操作符判断是否启用某个选项:

    if(status & EOCConnectionStateDisconnected) {   
    //EOCConnectionStateDisconnected is set 
    }
    

    Objcet-C中枚举

    typedof NS_NUM (NSUInteger,EOCConnectionState) {
     EOCConnectionStateDisconnected ,
     EOCConnectionStateConnecting,
     EOCConnectionStateDisconnected,
    };
    

    NS_NUM( _type, _name)是一个辅助宏,上面的语法展开如下,实际上就是C语言的枚举定义方式:

    typedf enum EOCConnectionState:NSUInteger EOCConnectionState;
    enum EOCConnectionState:NSUInteger { 
    EOCConnectionStateDisconnected , 
    EOCConnectionStateConnecting, 
    EOCConnectionStateDisconnected,
    };
    

    这里要注意,当需要枚举成员组合成新值得时候,也就是按位操作枚举成员的时候,我们一般使用NS_OPTIONS来定义枚举成员,与使用NS_NUM相比,NS_OPTIONS会帮我们在进行枚举成员的位运算过程中做隐式转换,保证枚举成员进行位运算的正确性

    typedof NS_OPTIONS (NSUInteger,EOCPerimtted) {
     EOCPerimttedDirectionUP = 1<<0, 
     EOCPerimttedDirectionDown = 1<<1, 
     EOCPerimttedDirectionLeft = 1<<2,
     EOCPerimttedDirectionRight= 1<<3,
    };
    
    使用场景:

    在表示一系列逻辑相似的状态,选项,我们应考虑使用枚举的方式,
    我们可以在SVProgressHUD这个项目的SVProgressHUD.h中找到如下的代码

    typedef NS_ENUM(NSUInteger, SVProgressHUDMaskType) {
       SVProgressHUDMaskTypeNone = 1, // allow user interactions while HUD is displayed
       SVProgressHUDMaskTypeClear,     // don't allow user interactions
       SVProgressHUDMaskTypeBlack,     // don't allow user interactions and dim the UI in the back of the HUD
       SVProgressHUDMaskTypeGradient   // don't allow user interactions and dim the UI with a a-la-alert-view background gradient
    };
    

    上面分别代表了SVProgressHUD几种显示样式

    使用建议:

    我们使用switch语法会习惯加上default分支,但在使用switch处理枚举类型数据相关逻辑时候,建议去掉default分值,这样做的好处是假如某个枚举值没有处理,编译器就会发出警告,提醒我们对所有的枚举选项都进行处理

    typedef NS_ENUM (NSUInteger, CWGConnectionState) {
       CWGConnectionStateDisconnected,
       CWGConnectionStateConnecting,
       CWGConnectionStateConnected,
    };
    switch (_currentState) {
     CWGConnectionStateDisconnected:
     // Handle connecting state
         break;
     CWGConnectionStateConnecting:
     // Handle disconnected state
         break;
     CWGConnectionStateConnected:
     // Handle connected state
         break;
    };
    

    相关文章

      网友评论

        本文标题:Effective Objective-C 2.0阅读笔记与实践

        本文链接:https://www.haomeiwen.com/subject/iaixkttx.html