1. ".h"文件中使用"向前声明"代替导入头文件
向前声明(forward declearing)
eg: @class EOCEmployer
不告诉编译器EOCEmployer类的全部细节,只告诉编译器有这个类名,在具体调用到这个累的方法或者属性的时候仍需要使用
#import "EOCEmployer.h"
优点:尽量延后头文件的导入时间(有利于提高文件的编译速度)
避免循环引用
注:若必须在头文件中引入其他头文件的时候:
1.) 需要引入头文件继承自某一父类,则头文件中需要导入该父类
2.) 需要遵循协议的时候
2. 关于协议的位置
1.) 最好把协议放在一个单独的头文件中,而不是集成在一个大的头文件中(避免循环引用问题)--委托协议除外。
委托协议:定义一个接口,某对象若想接受另一个对象的委托,则需要遵循其protrol(委托),以便成为其(delegate)委托对象。
2.) 委托协议名通常是(相关类名 + Delegate)
eg:TestProtocalDelegate
注 : delegate属性需要用weak来修饰(目的是避免循环引用)
3. delegate的OP用法
在委托模式和数据源的protocol中有一大部分方法是optional的:(这种方法因为可以不实现,所以在调用的时候)
if ( [ _delegaterespondsToSelector : @selector( doMethodA ) ] ) {
[ _delegaterespondsToSelector : @selector( doMethodA ) ];
}
这样做比较麻烦,如果我们在委托对应的类中添加一个枚举属性用于标识哪个可选方法可以相应的话就可以:
在签订delegate的类(即事先delegate方法的Class中):
[self.testObjdealWithMethodType:(MethodA|MethodB)];
在创建delegate的类中对应的testObjdealWithMethodType方法中:
- (void)dealWithMethodType:(MethodEnum)type{
self.type= type;
NSIntegernum = type;
/**为了避免提示case (MethodA | MethodB)的值在枚举中不存在的警告*/
switch(num) {
caseMethodA:
[self.delegatedoMethodA];
break;
caseMethodB:
[self.delegatedoMethodB];
break;
case(MethodA|MethodB):
[self.delegatedoMethodA];
[self.delegatedoMethodB];
break; }
}
4. 使用类型常量代替宏
类型常量的优点:
1.) 不会与系统的宏因为命名重复造成冲突
2.) 包含类型信息,能更好的描述含义
命名规范:
1.) 局限于某个.m文件的常量(k+常规命名)
2.) 全局常量:一般写在.h文件中(类名前缀+常规命名)
对外公开常量命名规范:(比如说注册的通知名)
eg:externNSString*constEOCStringConstent;//.h文件中声明
NSString*constEOCStringConstent =@"EOCStringConstentName";//.m文件中声明
常规命名习惯:
1.) 方法和变量名使用驼峰法命名:(清晰的方法名从左到右读起来好像一篇文章)
eg : 初始化一个矩形的方法名:
- (instancetype)initWithWidth:(CGFloat)width Length:(CGFloat)length;
方法命名注意事项:
1.) 如果方法的返回值是新创建的,那么方法的前缀应该是返回值类型,除非前面还有修饰语 注:属性的存取方法除外(get set 等)
eg:- (NSString*)stringByReplacingOccurrencesOfString:(NSString*)target withString:(NSString*)replacement
- (NSString*)lowercaseStringWithLocale:(nullableNSLocale*)locale
2.) 应该把表达参数类型的名词放在参数前面
eg:- (ObjectType)objectAtIndex:(NSUInteger)index;
3.) 如果方法要在当前的对象上执行操作,那么就应该包含动词,若执行动作的时候还需要操作对象,那应该在动词后跟一个或多个名词。
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(ObjectType)anObject;
4.) 不要使用str这种简称,使用string这种全程
5.) Boolean属性应该加is前缀,如果某方法返回分属性的Boolean值,那么应该根据其功能选has/is作为前缀
eg:@property(nonatomic,assign,getter=isDeal)Booleandeal;
- (BOOL)hasPrefix:(NSString*)str;
- (BOOL)isEqualToString:(NSString*)aString;
6.) 将get前缀留给借由输出的参数保存返回值的方法
eg:- (void)getObjects:(ObjectType__unsafe_unretained[])objects range:(NSRange)range;
5. 使用枚举表示:状态码,选项,状态
写在最后:我们在使用switch处理Enum语句的时候,不要再最后加default,这样的话在Enum中添加新的选项的时候编译器会识别并faculty警告,提示修改
6.属性与成员变量
1.) 尽量使用属性来代替成员变量
对象布局在编译器就已经固定了,(eg: 只要碰到访问_firstName变量的代码,编译器就会将其替换为(对应的)偏移量(offset))这个偏移量是 硬编码 ,表示该变量距离存放对象的内存区域的起始地址有多远。
oc应对某些代码库:使用了一份就得累的定义,但是其相连接的库使用了新的类的定义:
做法:吧实例变量当做一种存储偏移量的“特殊变量”交由 类对象 保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了。
这样甚至可以实现在运行期添加新的实例变量。
2.) 属性:一种标注写法。系统默认生成get,set方法的实例变量。可以访问封存在对象中的数据(并且编译器会自动生成一套存取方法)。
eg: @property (nonatomic,assign) Method Enumtype;
//等效
@interfaceTestProtocalClass(){
MethodEnumtype1;
}
- (MethodEnum)type1{
returntype1;
}
- (void)setType1:(MethodEnum)type{
type1 = type;
}
注:
1. 使用点语法和直接调用存取方法没有任何区别
2. 在类的实现中可以使用 @synthesizefirstName = myFirstName 实现指定实例变量的名字
3. 使用 @dynamictype; 可以实现创建属性的时候不为其创建存取方法,这时候使用代码访问属性不会报错(但要自己实现标准存取方法)
3.) 在读取实例变量的时候才用直接访问的形式,设置实例变量通过属性来做
1. 由于不经过方法派发,所以直接访问实例变量的速度更快。这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块区域。
2. 直接访问实例变量是不会调用其“设置方法”,这就绕过了为相关属性设置的“内存管理语义” (eg : 在ARC下直接访问一个copy的属性的时候不会拷贝该值,只会保留指向新值得指针,销毁原来的指针--类同声明为retain)
3. 直接访问实例变量不会触发KVO,通知(监听的是指针)
4. 通过属性 (有自定义的set/get方法) 有助于排查与之相关的错误,因为可以在set/get方法中设置断点,监调该属性的调用者和访问时机。
注:
1. 在初始化方法中设置属性的相关值的时候总应该直接访问实例变量 (因为 : 可能会出现子类重写set/get方法导致的异常)
2. 当使用懒加载的时候 (lazy initialization) 必须通过“get”方法访问属性,否则不会初始化 (懒加载不会调用) 。
要点:
1. 在对象内部读取数据时应该直接通过实例对象获取,写入数据的时候通过set属性方法
2. 在初始化和 delloc 中总应该直接通过实例变量读写数据。
3. 有时候会使用懒加载的方式配置数据,此时需要通过属性读取数据。
8. 对象等同性
"==" :比较的是两个对象的指针是否相等
"isEqual(id)" :比较的是两个OC对象是否相等
注:重写(override)isEqual方法
1. 重写isEqual设计逻辑:(当两个对象中所有的属性的值都相同的时候两个对象才相同)
- (BOOL)isEqual:(id)object{
if(self== object) {
return YES;
}
if ( [self class] != [object class]) {
return NO;
} else {
/**
添加两个值是否相等的判断逻辑
*/
return nil;
}
}
2. 实现hash方法
//简单的实现 :实现hash的目的是当isEqual实现是必须两个对象的hash返回值相同才执行
- (NSInteger)hash{
return1337;
}
//比较好的实现方法
- (NSUInteger)hash{
NSUIntegerfirstNameHash = [_firstName hash];
NSUIntegerlastNameHash = [_lastName hash];
NSUIntegerageHash = [_age hash];
return firstNameHash^lastNameHash^ageHash;
}
要点
1. set(集合)中直接存入是不可能存入两个相等的对象的,但是通过修改已经存在在集合中的某个对象,使之与集合中的另一个对象完全相等,这这个集合中就能够出现两个完全相等的对象,此时使用:(NSSet *copySet = [set copy])新的道德copySet中不存在重复的对象(冲的被去掉了一个)
2. 相同的对象必然有相同的hash,但是有相同的hash不一定代表是相同的对象(撞库)
9. 使用类工厂的方法实现类族
注:NSArray类的背后是类族(所有类似Array有可变不可变属性的都是类族)
!!!!这也就导致 if([mayBeAnArray Class] == [NSArray class]) 永远不可能成立
NSArray的初始化方法返回的是隐藏在类族公共接口后面的某个内部类型
1. 判断类的实例是否属于某个类(isMenberofClass)
2. 判断某个类的实例是否属于某个类族(isKindofClass)。