学任何东西都必须懂得原理,不然就只是会使用而已,如果不想当一个UI工程师,大家就一起好好看,好好学。
《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》写的是一些会用到的东西,也许我们知道该这么用,或者几种方法都可以用,但是不知道其中优劣,或者没有用过,这里将告诉你更好的方法。通过这本书的学习,可以解答开发中大多数的疑惑。提高开发效率,避免一些不必要的代码重构。
总结摘要版文章链接
一篇文章拿下《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》
细读链接
摘抄《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》
个人总结
我这里的总结只讲操作,不讲原理,想知道原理的话,看上面两篇文章就好了。
1,如果需要引用一个类文件时,只是需要使用类名,不需要知道其中细节,可以用@class xx.h,这样做的好处会减少一定的编译时间。
2,多用字面量语法,少用与之等价的方法
NSArray *languages = [NSArray arrayWithObjects:@"PHP", @"Objective-C", someObject, @"Swift", @"Python", nil];
//应该改为
NSArray *languages = @[@"PHP", @"Objective-C", someObject, @"Swift", @"Python"];
3,多用类型常量,少用#define预处理指令,多用枚举表示状态、选项、状态码
参考文章:https://www.jianshu.com/p/064b5b82a3a2
定义不对外公开的常量
static NSString * const kConst = @"Hello";
static const CGFloat kWidth = 10.0;
定义对外公开的常量
//Test.h
extern NSString * const CLASSNAMEconst;
//Test.m
NSString * const CLASSNAMEconst = @"hello";
枚举
typedef NS_ENUM(NSInteger,TestEnum) {
MY_INT_CONST = 12345
};
4,atomic 并不能保证多线程安全,所以iOS开发中属性都要声明为nonatomic,因为atomic严重影响了性能
5,不可变类型使用copy,可变类型使用strong,基础类型使用assign
@property (nonatomic, copy) NSArray *arrayOfCopy;
@property (nonatomic, strong) NSMutableArray *mutableArrayOfStrong;
@property (nonatomic, assign) BOOL isBool;
6,在对象内部尽量直接访问实例变量,使用_+属性,不走方法,速度更快
7,"对象等同性"
== 比较两个对象的内存地址
isEqualToString: 比较两个字符串的内容
isEqual: 默认情况下是比较两个对象的内存地址,但是有一些系统自带的类(比如Foundation中的NSString,NSArray等)重写了这个方法,改变了这个方法的判断规则(一般改为比较两个对象的内容,不是内存地址)
对于自定义对象而言,如果不重写isEqual方法,那么比较内容是一样的
因为isEqual,默认其实是比较hash值,自定义对象的hash值默认就是内存地址。
而我们比较自定义对象,一般是为了比较对象的属性是否相同,所以需要重写isEqual方法,重写isEqual方法需要重写hash。
代码示例
//MYPerson.m
/*
一旦重写了isEqual:方法,最好重写hash方法,而且要遵守以下原则:
1.isEqual:返回YES的2个对象,hash值一定要一样
2.hash值一样的2个对象,isEqual:返回不一定是YES
*/
@implementation MYPerson
- (NSUInteger)hash
{
return self.age + self.no + self.name.hash + self.car.hash;
}
- (BOOL)isEqual:(MYPerson *)object
{
return [self isEqualToPerson:object];
}
- (BOOL)isEqualToPerson:(MYPerson *)person
{
// 如果是完全相同的对象,就省去后面的判断
if (self == person) return YES;
// 如果object的类型不对,就不需要比较
if (![person isKindOfClass:self.class]) return NO;
// 基本数据类型
BOOL result = (self.age == person.age && self.no == person.no);
if (result == NO) return result;
// 对象类型,两个对象为nil时isEqual:的结果为0(NO),所以需要专门处理
if (self.name || person.name) {
if (![self.name isEqual:person.name]) return NO;
}
if (self.car || person.car) {
if (![self.car isEqual:person.car]) return NO;
}
return YES;
}
@end
8,类族简化对象创建
根据传入的不同状态,创建不同的类对象
NSArray是一个类族,创建出来的其实是NSArray的子类.
NSArray本身只是一个抽象基类,并不能创建对象。
if 永远为false
id maybeAnArray = @[];
if ([maybeAnArray class] == [NSArray class]) {
//Code will never be executed
}
代码示例
typedef NS_ENUM(NSUInteger, EOCEmployeeType) {
EOCEmployeeTypeDeveloper,
EOCEmployeeTypeDesigner,
EOCEmployeeTypeFinance,
};
@interface EOCEmployee : NSObject
@property (copy) NSString *name;
@property NSUInteger salary;
//Helper for creating Employee objects
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type;
//Make Employees do their respective day's work
- (void)doADaysWork;
@end
@implementation EOCEmployee
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type {
switch (type) {
case EOCEmployeeTypeDeveloper:
return [EOCEmployeeDeveloper new];
break;
case EOCEmployeeTypeDesigner:
return [EOCEmployeeDesigner new];
break;
case EOCEmployeeTypeFinance:
return [EOCEmployeeFinance new];
break;
}
}
- (void)doADaysWork {
//Subclasses implement this.
}
@end
9,关联对象
给对象关联其他对象,或者block。
objc_setAssociatedObject(关联对象,被关联对象的key,被关联对象, OBJC_ASSOCIATION_COPY);
根据被关联对象的key,来找到被关联对象。
//UIAlertViewDelegate protocol method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self doCancel];
}else {
[self doContinue];
}
}
\#import <objc/runtime.h>
static void *EOCMyAlertViewKey = @"EOCMyAlertViewKey";
- (void)askUserAQuestion {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" message: @"What do you want to do?" delegate: self cancelButtonTitle:@"Cancle" otherButtonTitles:@"Continue", nil];
void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
if (buttonIndex == 0) {
[self doCancel];
}else {
[self doContinue];
}
};
objc_setAssociatedObject(alert, EOCMyAlertViewKey, block, BJC_ASSOCIATION_COPY);
[alert show];
}
//UIAlertViewDelegate protocol method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey);
block(buttonIndex);
}
10,runtime - Method Swizzling( 交换方法)
看我这篇文章https://www.jianshu.com/p/551880b8feb0
11,用前缀避免命名空间冲突
12,提供“全能初始化方法”
13,实现description方法
我写的一个类重写了description方法,来直接打印对象的属性和属性值。
链接https://github.com/CDLOG/Object_description
14,尽量使用不可变对象
在对外属性声明的时候要尽量加上readonly修饰,如果外部想要修改,可以提供方法来进行修改。
15,使用清晰而协调的命名方式https://www.jianshu.com/p/bbb0b57eb168
16,为私有方法名加前缀,为类内的私有方法增加前缀
给私有方法的名称加上前缀,这样可以很容易地将其同公共方法区分开。在对于公共方法来说,修改其名称或签名之前要三思,因为类的公共API不便随意改动。如果改了,那么使用这个类的所有开发者都必须更新其代码才行。而对于内部方法来说,若要修改其名称或签名,则只需同时修改本类内部的相关代码即可,不会影响到面向外界的那些API。用前缀把私有方法标出来,这样很容易就能看出哪些方法可以随意修改,哪些不应轻易改动。
网友评论