美文网首页
iOS - 编写高质量的代码

iOS - 编写高质量的代码

作者: CDLOG | 来源:发表于2018-09-11 17:20 被阅读5次

学任何东西都必须懂得原理,不然就只是会使用而已,如果不想当一个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。用前缀把私有方法标出来,这样很容易就能看出哪些方法可以随意修改,哪些不应轻易改动。

相关文章

网友评论

      本文标题:iOS - 编写高质量的代码

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