《52个方法-第一章》

一、OC起源

  • OC使用的是“消息结构”而不是“函数调用”
  • 两者区别:
    • 消息结构:在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法
      1
      2
      NSObject *objc = [NSObject new];
      [objc performWith: parameter1 and: parameter2];
- 函数调用:由编译器决定,如果函数多态,编译时就要查处到底应该执行那个函数实现

1
2
Object *objc = new Object;
objc -> perform(parameter1, parameter2);
  • 消息结构中编译器不会关心接收消息的对象是何种类型,接收消息的对象问题也要在运行时处理,此过程叫做“动态绑定”

要点:

  1. OC语言使用的动态绑定的消息结构,在运行时所执行的代码由运行环境来决定,无论是否多态总会在运行时才会去查找所要执行的方法,
  2. OC 中对象总是分配在“堆空间”,而绝不会分配在”栈“上,不带 “*”的变量或者结构体等可能会使用“栈空间”

二、类头文件尽量少导入其他头文件

要点:

  1. 除非必要,否则不要引入头文件,使用向前声明降低类之间的耦合
  2. 有时无法使用向前声明,应该把该类遵守协议的声明放到“class-continuation分类”(匿名分类)中。
  3. 如果“class-continuation分类”也不行的话,就把协议单独放在一个文件中,再将其引入### 三、多用字面量语法,少用与之等价的方法

三、多用字面量语法,少用与之等价的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
- (void)tempMethond {
// OC1.0 起,支持简单方式创建基本类型对象

// - 使用字符串字面量:简介易读
NSString *someString = @"Effective Objective-C 2.0";
// 传统方式
NSString *someStrongOld = [NSString stringWithFormat:@"Effective Objective-C 2.0"];


// - 字面数值
NSNumber *someNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.1415926;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
// 字面量语法用于表达式
int x = 5;
float y = 6.23f;
NSNumber *expressionNumber = @(x * y);


// - 字面量数组
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];

// arrayWithObjects 和 字面量 的区别:
// arrayWithObjects 方法依次处理各个参数,直到发现nil为止
// 字面量数组遇到nil直接抛出异常
NSArray *animalsOld = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dogOld = [animalsOld objectAtIndex:1];


// - 字面量字典
// 如果有nil直接抛出异常
NSDictionary *personData = @{@"firtName": @"Matt",
@"lastName": @"Galloway",
@"age": @28};
NSString *lastName = personData[@"lastName"];

// dictionaryWithObjectsAndKeys 方法依次处理各个参数,直到发现nil为止
NSDictionary *personDataOld = [NSDictionary dictionaryWithObjectsAndKeys:@"Matt",@"firtName",
@"Galloway", @"lastName",
[NSNumber numberWithInteger:28], @"age", nil];

NSString *lastNameOld = [personData objectForKey:@"lastName"];


// - 可变数组和字典
NSMutableArray *mutableArray = animals.mutableCopy;
mutableArray[1] = @"dog";

NSMutableDictionary *mutableDict = [personData mutableCopy];
mutableDict[@"lastName"] = @"Galloway";
}

要点:

  1. 使用字面量语法创建字符串,数值,数组,字典简明扼要
  2. 通过下标操作获取数组,字典对应的元素
  3. 使用字面量创建数组,字典时若有nil ,会抛出异常,确保值里不含nil### 五、用枚举表示状态、选项、状态码

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

宏定义
  • 缺点:
    • 可以把所有相同的字符串值都替换为定义的值,
      • 定义出来的常量没有具体的类型信息
      • 如果有人重新定义了宏定义,不会有警告或者提醒,导致程序中值不一样
类型常量
  • 命名规则:

    • 如果只是在实现文件.m 中使用,则在前面加字母k
    • 如果此常量在类之外也可见,则以类名为前缀,(第19条详细了解命名习惯)
  • 声明位置:

    • 总是喜欢在头文件中声明宏定义,可能和常量名冲突,
    • 所有引入这个头文件的类都会出现宏定义或者常量,相当于声明了一个名叫kAnimationDuration的全局变量
    • 应该加上前缀,表明其所属的具体类,例如:EOCViewClassAnimationDuration
  • 常量从右往左解读依次是: 具体类型,不可修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	// const 修饰的是常量不希望别人修改, NSString * 指向NSString对象
    extern NSString *const EOCStringConstant;


    // 两者写法都可以
    extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
    extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    ```

    - 如果不打算公开常量,可以定义在 .m 实现文件里

    static const NSTimeInterval kAnimationDuration = 0.3;

    1
    2
    3

    - static关键字
    - static 修饰意味着该变量仅在定义此变量的编译单元(实现文件.m)中可见,作用域就是当前.m文件
    static const NSTimeInterval kAnimationDuration = 0.3;
    
    1
    2
    3
    - 如果需要声明一个外部可见的常值变量,需要在头文件中声明并使用 extern 关键词修饰 "extern 

    在 .h 文件中
    extern NSString *const EOCStringConstant; // 两者写法都可以 extern const NSTimeInterval EOCAnimatedViewAnimationDuration; extern NSTimeInterval const EOCAnimatedViewAnimationDuration1;
    1
    2

    在 .m 文件中
    NSString *const EOCStringConstant = @"VALUE"; const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3; NSTimeInterval const EOCAnimatedViewAnimationDuration1 = 0.3;
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **要点:**

    1. 定义常量时static const定义编译单元(实现文件.m)中可见的常量,无需为其名称加前缀
    2. 在头文件中使用extern 声明全局常量,并在实现文件中定义其值,这种常量要出现在全局符号表中,所以加相关类作为前缀

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

    - 普通枚举

    enum EOCConnectionState {

    EOCConnectionStateDisconnected, // 断开连接
    EOCConnectionStateConnecting,   // 连接中...
    EOCConnectionStateConnected,    // 已连接
    

    };

    1
    普通枚举如果要使用如下:

    enum EOCConnectionState status = EOCConnectionStateConnected;

    1
    2

    如果每次都不需要输入enum关键字,只需要typedef关键字重新定义枚举类型

    EOCConnectionState status1 = EOCConnectionStateConnecting;

    1
    2

    - 设置枚举的初始值

    enum EOCConnectionStateConnectionState {

    EOCConnectionStateConnectionStateDisconnected = 1, // 断开连接
    EOCConnectionStateConnectionStateConnecting,   // 连接中...
    EOCConnectionStateConnectionStateConnected,    // 已连接
    

    };

    1
    2
    3
    4
    5

    - 可选枚举

    - << 位操作符(左移), 表示往左移动N位,使用 位运算符 | (或)来组合使用
    - 使用位运算符 &(与) 判断是否开启了某个选项

    enum UIViewAutoresizing {

    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    

    }

    1
    2
    3
    4

    - 使用宏定义枚举

    普通枚举类型(老式语法定义枚举)

    typedef enum : NSUInteger {

    EOCConnectionState2DisConnected,
    EOCConnectionState2DisConnecting,
    EOCConnectionState2Connected,
    

    } EOCConnectionState2;

    1
    2

    NS_ENUM 宏定义的枚举展开后:

    typedef enum EOCConnectionState_3 : NSUInteger EOCConnectionState_3;
    enum EOCConnectionState_3 : NSUInteger {

    EOCConnectionState_3DisConnected,
    EOCConnectionState_3DisConnecting,
    EOCConnectionState_3Connected,
    

    };

    1
    2

    - 定义新特性枚举

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    EOCConnectionState3DisConnected,
    EOCConnectionState3DisConnecting,
    EOCConnectionState3Connected,
    

    };

    1
    2
    3
    4

    - 枚举使用

    .h文件

    #import <UIKit/UIKit.h>

    typedef enum EOCConnectionState EOCConnectionState;

    typedef NS_ENUM(NSUInteger, EOCConnectionState3) {

    EOCConnectionState3DisConnected,
    EOCConnectionState3DisConnecting,
    EOCConnectionState3Connected,
    

    };

@interface EOCEnum : NSObject
@property (nonatomic, assign) enum EOCConnectionState state;
@property (nonatomic, assign) EOCConnectionState3 state3;
@end

1
2

.m 文件中
#import "EOCEnum.h" @implementation EOCEnum - (void)testEnum { // 未使用typedef 关键字 enum EOCConnectionState status = EOCConnectionStateConnected; NSLog(@"%u", status); // 使用typedef 关键字 EOCConnectionState status1 = EOCConnectionStateConnecting; NSLog(@"%u", status1); UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (resizing & UIViewAutoresizingFlexibleWidth) { // 设置了 UIViewAutoresizingFlexibleWidth 约束 } } // UIKit 试图所支持的设置显示方向 - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; } // 枚举使用 - (void)userEnum { switch (_state3) { case EOCConnectionState3DisConnected: // ... break; case EOCConnectionState3DisConnecting: // ... break; case EOCConnectionState3Connected: // ... break; default: // 最好不用,防止以后增加一种状态机之后,编译器不发出警告 break; } } @end ```

要点:

  1. 使用枚举表示状态机的状态、传递给方法的选项以及状态码等值,给枚举值起名时要注重易懂
  2. 如果一个类型可以使用状态机的状态来表示,并且多个选项可同时使用,那么就定义为可选的枚举类型,枚举各值定义为2的幂,以便通过“按位或”操作组合
  3. 使用NS_ENUM 和 NS_OPTIONS 宏来定义枚举,并指明底层的数据类型。这样可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型
  4. switch语句处理枚举类型时不要实现default 分支,如果以后加入枚举的新状态之后,编译器会发出警告