effective OC2.0 阅读笔记 (一 熟悉Objective-C)

第一条:了解Objective-C语言的起源

  1. Objective-C从Smalltalk语言是从Smalltalk语言演化而来,Smalltalk是消息语言的鼻祖。
  2. 所有的OC语言的对象都必须这样:NSString *someString = @"The string"; 因为对象所占内存总是分配在“堆空间(heap space)”中,而绝不会分配在"栈(stack)上"。不能像这样:NSString stackString; 分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理。
  3. 有时会遇到不含*的变量,他们可能会使用“栈空间”,这些变量所保存的不是OC对象。比如CGRect.frame.origin.x = 0.0f类似的 CGRect时C的结构体,其定义如下
    整个系统框架都在使用这种结构体,因为如果改用OC对象来做,性能会受影响。与创建结构体相比,创建对象还需要额外开销,例如分配及释放堆内存等。
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
  1. Objective-C是C语言的超集,c语言中的所有功能在编写OC代码时依然适用。在C语言基础上添加了面向对象等特性,可能一开始接触时你会觉得语法有点奇怪,那是因为Objective-C使用了动态绑定的消息结构,而Java,C++等等语言使用的是函数调用。
  2. 消息结构与函数调用的关键区别在于:函数调用的语言,在编译阶段由编译器生成一些虚方法表,在运行时从这个表找到所要执行的方法去执行。而使用了动态绑定的消息结构在运行时接到一条消息,接下来要执行什么代码是运行期环境决定的,而不是编译器。

第二条:在类的头文件中尽量少引入其他头文件

  1. 如果需要引用一个类文件时,只是需要使用类名,不需要知道其中细节,可以用@class xx,这样做的好处会减少一定的编译时间。如果是用的#import全部导入的话,会出现a.h import了b.h,当c.h 又import a.h时,把b.h也都导入了,如果只是用到类名,真的比较浪费,也不够优雅
  2. 有时候无法使用@class向前声明,比如某个类要遵循一项协议,这个协议在另外一个类中声明的,可以将协议这部分单独放在一个头文件,或者放在分类当中,以降低引用成本。

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

多使用字面量语法来创建数值,字符串,数组,字典等,好处是:使代码更简洁,易读,也会避免nil问题。
例如:

数值:

NSNumber *intNumber = @1;
NSNumber *intNumber = @2.5f;
NSNumber *intNumber = @3.1415926;
NSNumber *intNumber = @YES;
NSNumber *intNumber = @'a';

数组:

NSArray *animals = @[@"cat",@"dog",@"mouse",@"badger"];
NSString *dog = animals[1];

如果正常创建 如下

NSArray *arrayA = [NSArray arrayWithObjects:object1,object2,object3,nil];

如果1,3指向有效的OC对象,而2是nil,那么正常创建只含有1一个对象,字面量语法创建时会抛出异常。原因在于arrayWithObjects这个方法会依次处理参数,直到遇到nil,由于2是nil,所以会提前结束。所以使用字面量语法更安全,我们可以通过异常更快发现这个错误。

字面量字典:

字典是一种映射型数据结构,可向其中添加键值对。

NSDictionary *personData = @{@"first" : @"Matt" , @"last" : @"Galloway" , @"age" : @28};

如果使用正常的dictionaryWithObjectsAndKeys和之前的那个数组一样,会在首个nil之前停下,并抛出异常,这有助于查错。
同样的,字典也可以像数组那样用字面量语法访问。NSString *lastName = personData[@"first"];

字符串:

通过取下标的方法,我们可以访问数组中某个下标或字典中某个键所对应的元素。如果是可变类型的,我们也可以通过下标修改其中的元素值:
标准做法

[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableArray setObject:@"Galloway" forKey:@"last"];
//换做取下标来写,则是:
mutableArray[1] = @"dog";
mutableDictionary[@"last"] = @"Galloway";

局限性:

使用字面量语法创建出来的字符串、数组、字典对象都是不可变的。若想要可变版本的对象,则需要复制一份:NSMutableArray *mutable = [@[@1,@2,@3,@4,@5]mutableCopy];

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

应该使用

static constant NSTimeInterval kAnimationDuration = 0.3;

不要使用

#define Animation_duration 0.3

原因:这样定义出来的常量没有类型信息,假设这个指令在某个头文件中,那么所有引入了这个头文件的代码,其都会被替换。

  1. 不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即时有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。
  2. 变量一定要同时用static和const来声明。
    当不公开某个常量时,在.m文件中使用static const定义
    static const NSTimeInterval KAnimationDuration = 0.3;
    这种方式定义的常量含有类型信息,const修饰符确保变量不会被修改,static修饰符使该全局变量的作用域仅限于当前文件。由于此类常量不在全局符号表中,所以无须为其名称加前缀。
  3. 在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常用与之相关的类名做前缀。
//In the header file
 extern NSString *constant EOCStringConstant;//这里用UIKIT_EXTERN会比较好
//In the implementation file
NSString *const EOCStringConstant = @"Value";

常量定义应从右至左解读,EOCStringConstant就是一个常量,而这个常量是指针,指向NSString对象。(大概意思应该是,const修饰的是常量,那么EOCStringConstant是一个常量,而*是一个指针,最后,它的类型是NSString。)

第五条:用枚举表示状态、选项、状态码

枚举是C语言中的一种基本数据类型,是一个"被命名的整型常量"的集合,它不参与内存的占用和释放,我们在开发中使用枚举的目的只有一个,那就是为了增加代码的可读性。在以一系列常量来表示错误状态码或可组合的选项、状态时,极宜使用枚举为其命名。

在Objective-C语言中,Apple在iOS6中引入了两个宏来重新定义枚举类型(即:NS_ENUM与NS_OPTIONS),这两者在本质上并没有差别,都是用于定义枚举类型,但是在使用中NS_ENUM多用于一般枚举,而NS_OPTIONS则多用于带有移位运算的枚举

注:什么是移位运算?
比如 : 2<<5
意思是:把2换成2进制(…0 0 0 0 0 1 0),然后<<的意思是向左移动,
那么后面是5的话,就是移动5位,那么结果就是— 64 了。

NS_ENUM使用示例

typedef NS_ENUM(NSInteger, Test)
{
    TestA = 0,
    TestB,
    TestC,
    TestD
};

NS_OPTIONS使用示例

typedef NS_OPTIONS(NSUInteger, Test) {
    TestA = 1 << 0,
    TestB = 1 << 1,
    TestC = 1 << 2,
    TestD = 1 << 3
};
typedef NS_OPTIONS(NSUInteger, Test) {
    TestNone = 0,
    TestA = 1 << 0,
    TestB = 1 << 1,
    TestC = 1 << 2,
    TestD = 1 << 3
};

带有移位运算的枚举多用于同一个枚举变量可以同时赋值多个枚举成员的情况,其原理就是将各个枚举值按位或(|),因为移位运算的枚举成员可以保证按位或(|)计算之后结果的唯一性,所以每种结果都能反向计算出是由哪几个枚举成员按位或(|)而成

按位与运算符的应用

- (void)todo:(UIViewAutoresizing)type {
   if (type & UIViewAutoresizingFlexibleLeftMargin) {
      NSLog(@"UIViewAutoresizingFlexibleLeftMargin");
   }
}
传入参数type 为 UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleHeight

上文知道结果是 0001 0101,十进制值得21
UIViewAutoresizingFlexibleRightMargin为4

0001 0101 & UIViewAutoresizingFlexibleRightMargin = 0001 0101 & 0000 0100 =21 & 4 = 4 
根据计算结果还是UIViewAutoresizingFlexibleRightMargin这个枚举
所以理论上更严谨的写法应该是:
if ((type & UIViewAutoresizingFlexibleRightMargin) == UIViewAutoresizingFlexibleRightMargin){
}

要点

  1. 应该用枚举来表示状态机的状态、传递给方法的选项以及状态码等值,给这些值起个易懂的名字。
  2. 如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为2的幂(2^n),以便通过按位或操作将其组合起来。(其实最终的结果就是移位运算 比如2->10;4->100;8->1000;16->10000)
  3. 用NS_ENUM与NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
  4. 在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值