美文网首页
Effective Objective-C 2.0随身笔记(一)

Effective Objective-C 2.0随身笔记(一)

作者: Ryan_RH | 来源:发表于2017-02-09 10:26 被阅读0次

(注:该笔记适用于结合Effective Objective-C 2.0这本书一起看,笔者只是整理了其中的知识点,细致的地方还望大家在原著上查看,还望能帮助到大家)

一. 熟悉Objective-C

1.OC语言的起源

OC使用的是“消息结构”而不是“函数调用”。


消息与函数调用之间的区别:使用消息结构的语言,其运行时所执行的代码由运行环境来决定;使用函数调用的语言,则由编译器决定。


OC的重要工作都由“运行期组件”而非编译器来完成。


“运行期组件”的本质就是一种将开发者所编代码相链接的“动态库”。


NSString *str = @"string";

它声明了一个名为str的变量,其类型为NSString*,也就是说此变量为指向NSString的指针。所有OC语言的对象都必须这样声明,因为对象所占的内存总是分配在“堆空间”中,而绝不会分配在“栈”上。如果这时再创建一个变量指向同一个地址,那么并不拷贝该对象,只是这两个变量会同时指向此对象。


分配在栈中的内存会在栈帧弹出时自动清理,而分配在堆中的内存则被抽象出来,也就是我们常叫的“引用计数”。

2.在类的头文件中尽量少引用其他头文件

将引入头文件的时机尽量推延,只有在确切需要使用时才引入,这样可以减少类的使用者所需引入头文件的数量。

方法:在头文件“向前声明”该类;#import "xx" 改用为 @class xx;然后在实现文件再#import "xx"。

3.多用字面量语法,少用与之等价的方法

比如:NSNumber *num = @1;     int x = 5;     NSArray *arr = @[@"one",@"two"];     NSString *one = arr[0];     NSDictionary *dic = @{@"one":@"1",@"two":@"2"};     NSString *one = dic[@"one"];

注意:用字面量语法创建数组或者字典时,若值中有nil,则会抛出异常。因为请务必确保值里不含nil。

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

定义常量时,我们有时会:#define ANIMATION_DURATION 0.3

可以换成:static const NSTimeInterval kAnimationDuration = 0.3;

这种写法更清楚描述了常量的含义和所用到的类型。static修饰符意味着改变了仅在定义此变量的编译单元内可见,const则是不允许此常量被修改。

命名规范:若常量局限于某“编译单元”之内,则在前面加字母k;若常量在类之外可见,则通常以类名为前缀。


如果需要对外公开某个变量,比如要派发通知需要此通知的名称常量,可以这样定义:

在头文件中extern NSString *const EOCStringConstant;

在实现文件中NSString *const EOCStringConstant = @"VALUE";

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

enum EOCConectionState{

EOCConectionStateConnecting,

EOCConectionStateDisconnected,

}

二、对象、消息、运行期

6.理解“属性”这一概念

“属性”是OC的特性,用于封装对象中的数据。OC对象通常会把其所需要的数据保存为各种实例变量,其中getter用于读取变量值,setter用于写入变量值。


@property语法来定义对象中所封装的数据,其意思是:编译器会自动写出一套存取方法,用以访问给定类型具有给定名称的变量。访问属性可以通过“点语法”,效果跟直接调用存取方法一致。

比如:@property NSString *firstName;

相当于-(NSString *)firstName;

-(void)setFirstName:(NSString *)firstName;

属性特性

原子性、读/写权限、内存管理语义、方法名

1.基本所有属性都要声明为nonatomic,因为如果使用atomic在iOS上开销较大,会带来性能问题,而且atomic也并不能保证“线程安全”,若要保证线程安全则需要采用更深层的锁定机制,但在Mac OSX程序上,atomic并不会有性能瓶颈。

2.具有readwrite特性的属性有getter和setter方法,如果是readonly特性的属性只有getter方法。

3.assign针对纯量类型(CGFloat、NSInteger等);

strong表明该属性定义了一种“拥有关系”,为这个属性设置新值时会先保留新值并释放旧值,再将新值设置上去;

weak表明该属性定义了一种“非拥有关系”,为这种属性设置新值时,设置方法既不保留新值也不释放旧值,特性跟assign一样,然而在属性所指向的对象被摧毁时,属性值也会被置空nil;

unsafe_unretained特性和assign相同,但是它适用于“对象类型”,该特性表明了一种“非拥有关系”(unretained),当目标对象被摧毁,属性值是不会被置空nil(unsafe);

copy特性和strong类似,然而设置方法并不保留新值,而是将其拷贝,常用于NSString来保护其封装性。因为传递给设置方法的新值有可能指向一个NSMutableString类的实例,这个类是NSString的子类,可以修改其值的字符串,若此时不拷贝字符串就有可能被修改,所以只要实现属性所用的对象是可变的,就应该在设置新属性时拷贝一份

4.getter=<name>指定“获取方法”的方法名:

@property(nonatomic, getter=isOn)BOOL on;


setter = <name>“设置方法”的方法名:

-(id)initWithFirstName:(NSString *)firstName{

if(self = [super init]){

_firstName = [firstName copy];

}

}

7.在对象内部尽量直接访问实例变量

通过属性访问和直接访问实例变量有以下区别:

(一)直接访问实例变量速度更快,编译器所生成的代码会直接访问保存对象实例变量的内存。

(二)直接访问实例变量时,不会调用其setter方法,这就绕过了为相关属性所定义的“内存管理语义”。比方说,在ARC下直接访问一个声明copy的属性,那么并不会拷贝该属性,只会保留新值并释放旧值。

(三)直接访问实例变量不会触发KVO。

(四)通过属性访问有助于排查与之相关的错误,因为可以在getter和setter中加断点来调试。


总结:

1.在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,应该通过属性来写。

2.在初始化以及dealloc方法中,总是应该直接通过实例变量来读写数据。

3.使用懒加载初始化时,需要通过属性来读取数据,否则实例变量永远不会初始化。

-(EOCBrain*)brain{

if(!brain){

_brain = [Brain new];

}

}

8.理解“对象等同性”这一概念

==操作符比较的是两个指针的本身,若想检测对象的等同性,请提供“isEqual”与hash方法。

相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。

不要盲目地逐个检测每条属性,而是应该依照具体需求来制定检测方案。

编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。

9.以“类族模式”隐藏实现细节

类族模式可以把实现细节隐藏在一套简单的公共接口后面。

Cocoa里面的类族,比如NSArray,NSMutableArray合起来算一个类族,大部分的collection类都是类族。

创建一个类族需要枚举对象、工厂方法和一套公共的接口,在工厂方法里面根据不同的枚举对象返回不同的子类,其中子类应该继承自类族的抽象基类,子类应该定义自己的存取方法,子类应该覆写超类文档中指明需要覆写的方法。

10.在既有类中使用关联对象存放自定义数据

可以通过“关联对象”机制将两个对象关联起来:

objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy)

objc_getAssociatedObject(id object, void *key)


比如可以应用在UIAlertView上,关联block,这样创建视图和处理操作的代码就在一起,比原来更简单易懂。


注意:这种做法通常会引入难于查找的BUG。

未完待续。

相关文章

网友评论

      本文标题:Effective Objective-C 2.0随身笔记(一)

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