《EffectiveObjective-C2.0:52个有效⽅法》(全)笔记《Effec tive Objec tive-C 2.0:编写⾼质量iOS与OS X代码的52个有效⽅法》
在看这本书的时候,对每⼀个章节⾥⾯的例⼦,都有⼀⼀去验证,并对⼀些验证记录在笔记。
在项⽬中很多场景下都有尝试去⽤,效果还是⽐较可观的。
只记录有⽤的。
1 -> Objec tive-C的特性
Objective-C:消息结构语⾔,运⾏时所执⾏的代码由运⾏时环境决定;
C++:使⽤函数调⽤的语⾔,由编译器决定。
⼩结:
Objective-C为C语⾔添加了⾯向对象特性,是其超集;Objective-C使⽤动态绑定的消息结构,在运⾏时才会检查对象类型,接收到消息后,执⾏代码,由运⾏期的环境⽽⾮编译器来决定。
2 -> 在类的头⽂件中尽量少引⽤其它头⽂件
#import<Foundation/Foundation.h>
#import“JDSEmployer.h” (这样会⼀并引⼊该⽂件的所有的内容,增加编译时间)
@class JDSEmployer; (“向前申明”):可以节省编译时间,将引⼊头⽂件的时间尽量延后,只在确实需要时才引⼊;降低相互依赖问题;向前申明也可以解决两个类相互引⽤的问题
#include "JDSPerson.h"
@interfaceJDSPerson :NSObject
@property(nonatomic,copy)NSString*firstName;
@property(nonatomic,copy)NSString*lastName;
@property(nonatomic,strong)JDSEmployer*employer;
@end
⼩结:
1、必要的情况下,才引⽤头⽂件;⼀般,应在某个类的头⽂件中使⽤向前声明来提及别的类,并在实现⽂件中引⼊那些类的头⽂件,来降低类之间的耦合。
object to2、⽆法适⽤向前声明时,⽐如要声明某个类遵循的协议,⽐如要声明某个类遵循⼀项协议。这种情况下,尽量把“该类遵循某协议”的这条声明移⾄“class-continuation分类”中。如果不⾏的话,就把协议单独放在⼀个头⽂件中,然后将其引⼊。
3 -> 多⽤字⾯量语法,少⽤与之等价的⽅法
NSNumber*someNumber = [NSNumbernumberWithInt:1];
NSArray*animals = [NSArrayarrayWithObjects:@"cat",@"dog",nil];
使⽤下⾯的字⾯量语法代码更整洁。
NSArray*animals =@[@"cat",@"dog"];NSNumber*someNumber =@1;  idobj1 =@"1";  idobj2 =nil;  idobj3 =@"3"; NSArray*arrayA = [NSArrayarrayWithObjects:obj1,obj2,obj3,nil];
这种⽅法不会奔溃,⼀次处理到nil时就会结束
NSLog(@"%@",arrayA);打印:(1)
NSArray*arrayB =@[obj1,obj2,obj3];
这种⽅法会抛出异常 NSLog(@"%@",arrayB);
打印:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -
[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1]'
⼩结:
1、应该使⽤字⾯量语法来创建字符串、数值、数组、字典。与创建此类对象的常规⽅法相⽐,这么做更加简明扼要。
2、应该通过取下标操作来访问数组下标或字典中的键所对应的元素。
3、⽤字⾯量语法创建数组或字典时,若值中有nil,则会抛出异常。因此,务必确保值⾥不含nil。
4- >多⽤类型常量,少⽤ #define 预处理指令
static const NSTimeIntervalkAnimationDuration =0.3;
试图修改 这个常量,编译就会报错
不加static:另⼀个⽂件声明同样的变量名,编译器会报错:
duplicate symbol _kAnimationDuration in:
EOCAnimatedView.o
EOCOtherView.o
如果⼀个变量既声明为static,⼜声明为const,那么编译器根本不会创建符号,⽽是会像#define预处理指令⼀样,把所有遇到的变量都替换为常值。不过还是要记住:⽤这种⽅式定义的常量带有类型信息。
.m实现⽂件中这样去声明⼀个变量:NSString *const EOCStringConstant = @“VALUE”,那么需要给外部⽂件使⽤的话,这样写:extern
NSString *const EOCStringConstant;
.h    extern NSString*constnotyfication;
.m  NSString  *constnotyfication =@"notyfication";
上⾯的例⼦可以写为:
// EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
// EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;
⼩结:
1、不要⽤#define定义常量,只是代码替换,并没有减少代码量,有⼈重新定义此常量编译器也不会报错,导致常量值出错。
2、在实现⽂件中使⽤static const来定义“只在编译单元内可见的常量”(translation-unit-specific constant)。由于此类常量不在全局符号表中,所以⽆须为其名称加前缀,在头⽂件中使⽤extern来声明全局常量,并在相关实现⽂件中定义其值。这种常量要出现在全局符号表中,所以其名称应加以区隔,通常⽤与之相关的类名做前缀。
3、const 不可变原则 :其右边的总不能被修改,能提⾼代码的安全性,降低程序员的沟通成本。
5 -> ⽤枚举表⽰选项、状态码
typedefNS_ENUM(NSUInteger, JSConnectionState) {
JSConnectionStateDisconnected,
JSConnectionStatingConnecting,
JSDisConnectionStatedConnected,
};
⽤命名描述清楚,状态值含义;
⽤枚举作为switch 参数;可以不⽤写default分⽀:如上图所⽰,在枚举中添加的新的值⽽⼜未被使⽤就会发出警告
⼩结:
1、应该⽤枚举来表⽰状态机的状态、传递给⽅法的选项以及状态码等值,给这些值起⼀个易懂的名字。
2、多个枚举状态可以同时使⽤,应该将选项值定义为2的冥,以便通过通过按位或操作将其组合起来
3、不需要枚举值互相组合⽤NS_ENUM, 如果需要互相组合⽤NS_OPTIONS,NS_ENUM、NS_OPTIONS:需要指明其底层数据结构,可以确保是程序员想要的数据类型,⽽不是编译器所选的类型。
4、处理switch放语句,不需要实现default分⽀,加⼊新的枚举值后,编译器会报错:switch并为处理所有枚举。
6 -> 理解“属性”这⼀概念
1、原⼦性
atomic:原⼦属性,默认就是atomic。需要消耗⼤量的资源。
nonatomic:⾮原⼦属性。适合内存⼩的移动设备。
默认情况下,由编译器所合成(synthesize)的⽅法会通过锁定机制来确保它是原⼦的(atomic)。如果属性具有nonatomic特质,则不使⽤同步锁。
需要注意的是,开发iOS程序时⼀般都会使⽤nonatomic属性,这是因为在ios中使⽤同步锁的开销较⼤,这会带来性能问题。但是在Mac OS X 中,使⽤atomic属性通常都不会有性能瓶颈。
⼀般情况下,并不要求属性必须是原⼦的,因为这并不能保证“线程安全”,若要实现线程安全,需要更为深层的锁定机制才⾏。例如⼀个线程要连续多次读取某属性的过程中,另⼀个线程对该属性值进⾏了修改,那么即便将属性声明为atomic,还是会读到不同的属性值。
2、读/写权限
readwirte:表明属性具有读取和设置⽅法。
readonly:  表明属性只拥有读取⽅法
若属性由@synthesize实现,则编译器才会⾃动合成与其读写权限相关的⽅法。
3、内存管理语义
内存管理语义仅会影响属性的“设置⽅法”。编译器在合成存取⽅法时,要根据此特质来决定所⽣成的代码。如果⾃⼰来编写存取⽅法,那么就必须与相关属性所声明的特质相符。
assign:只执⾏针对“纯量类型”(scalar type,例如CGFloat或NSInteger等)的简单赋值操作。
strong:表明该属性定义了⼀种拥有关系。为该属性设置新值时,会先保留新值,并释放旧值,最后再设置新值。
strong
weak:表明该属性定义了⼀种⾮拥有关系。为该属性设置新值时,既不保留新值,也不释放旧值。此特质跟assign类似,然⽽在属性所指对象被weak
销毁时,该属性也会清空(nil out)。
unsafe-unretained:此特质的语义跟assign相同,但它适⽤于对象类型。表明该属性定义了⼀种⾮拥有关系,在属性所指对象被销毁时,该unsafe-unretained
属性不会⾃动清空,这点跟weak不同。
copy:此特质所表达的所属关系跟strong类似。然⽽设置⽅法并不保留新值,⽽是将其拷贝。只要实现属性所⽤的对象是可变的(mutable),就应该在设置新属性值时拷贝⼀份。当属性类型为NSString*时,经常⽤此特质来保护其封装性,因为传递给设置⽅法的新值有可能是NSMutableString类型的实例。
4、⽅法名
可通过如下特质来指定存取⽅法的⽅法名。
getter=:指定“获取⽅法”的⽅法名。
getter=
例如:@property(nonatomic,getter=isOn)BOOLon;
setter=:指定“设置⽅法”的⽅法名,这种⽤法不太常见。
setter=
⼩结:
1、通过“特质”来指定存储数据所需的正确语义。
2、开发 iOS 程序时应使⽤ nonatomic 属性,因为 atomic 属性会严重影响性能。
7 -> 在对象内部尽量直接访问实例变量
NSString*oldName1 =self.firstName; //通过属性访问
NSString*oldName2 = _firstName;    //  直接访问
⼩结:
1、在对象内部读取数据时,应该直接访问实例变量来读,写⼊数据时,则应该通过属性来写
2、在初始化⽅法和 dealloc⽅法中,总是应该直接通过实例变量来读写数据
3、有时会使⽤惰性初始化技术配置某分数据,那么必须通过属性来读取数据
4、直接访问实例变量,不会掉⽤“设置⽅法”,内存管理语义就没⽤了,相同不会触发“键值对-KVO”
8 -> 理解“对象等同性”这⼀概念
⽐较对象的等同性是⼀个⾮常有⽤的功能。不过按照==操作符⽐较的是⽐较两个对象的指针地址,⽽不是其所指的对象。应该使⽤NSObject协议中的isEqual⽅法来判断两个对象的等同性。NSObject类对isEqual⽅法的默认实现是当且仅当两个对象的指针值相等时,才判定这两个对象相等,这时hash⽅法返回的值也必须相等。
例如:
- (BOOL)isEqual:(id)object{
if([selfclass] == [objectclass]) {
return[selfisEqualToPerson:(JDSPerson*)object];
}else{
return[superisEqual:object];
}
returnNO;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。