Effective OC

作者: tongxyj | 来源:发表于2016-01-14 21:05 被阅读76次

    1.OC为C语言添加了面向对象特性,是其超集。OC使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一条消息后,究竟应执行何种代码,由运行期环境而非编译器决定。
    2.在类的头文件中尽量少引用其他头文件

    • 除非有必要,否则不要引入头文件,应在.h中使用@class声明别的类,并在.m中引入该类的头文件。这样做可以降低类之间的耦合。
    • 有时无法使用@class声明,比如要声明某个类遵循一项协议。这种情况下,尽量把“该类遵循某项协议”的这条声明移至“分类中”
      3.多用字面量语法(@[@"xx"]);
      用字面量语法创建数组或字典时,若值中又nil,则会抛出异常。因此,务必确保值里不含nil。
      4.多用类型常量,少用#define预处理指令
    • 不要用与预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。
      只在.m中使用,可这样定义:
    static const NSTimeInterval kAnimationDuration = 0.3;
    

    若要让外部使用,如下:
    .h

    extern NSString *const EOCStringConstant;
    

    .m

    NSString *const EOCStringConstant = @"xxx";
    

    5.用NS_ENUM与NS_OPTIONS宏来定义枚举类型。在处理枚举类型的switch语句中不要事先default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。
    6.在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。

    • 在初始化及dealloc方法中,总是应该直接通过实例变量来读写数据。
      7.创建类簇
      首先定义抽象基类:
    typedef NS_ENUM(NSUInteger,EOCEmployeeType) {
        EOCEmployeeTypeDeveloper,
        EOCEmployeeTypeTypeDesigner,
        EOCEmployeeTypeFinace,
    };
    

    抽象基类.m

    +(EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
         switch (type) {
            case EOCEmployeeTypeDeveloper:
                return [EOCEmployeeDeveloper new];
                break;
            case EOCEmployeeTypeDesigner:
                return [EOCEmployeeDeveloper new];
                break;
            case EOCEmployeeTypeFinace:
                return [EOCEmployeeDeveloper new];
                break;
        }
    }
    
    -(void)doADaysWork {
    }
    

    // 子类

    -(void)doAdaysWork {
      [self writeCode];
    }
    
    命名规范

    8.如果方法的返回值是新创建的,那么方法名的首个词应是返回值的类型,除非前面还有修饰语,例如localizedString。属性的存取方法不遵循这种命名方式。

    9.应该把表示参数类型的名词放在参数前面。

    10.如果方法要在当前对象上执行操作,那么就应该包含动词;若执行操作时还需要参数,则应该在动词后面加上一个或多个名词。

    11.不要使用str这种简称,应该用string这样的全程。

    12.boolean属性应加is前缀。如果某方法返回非属性的boolean值,那么应该根据其功能,选用has或is当前缀。

    13.copy&mutableCopy
    深拷贝的意思就是:在拷贝对象时,将其底层数据也一并复制过去。Foundation框架中所有的collection类在默认情况下都执行浅拷贝,也就是说,只拷贝容器对象本身,而不复制其中数据。

    - (instancetype)initWithXing:(NSString *)xing ming:(NSString *)ming {
        if (self = [super init]) {
            _xing = [xing copy];
            _ming = [ming copy];
            _friends = [NSMutableArray array];
        }
        return self;
    }
    - (id)copyWithZone:(NSZone *)zone {
        Person *person = [[Person allocWithZone:zone] initWithXing:_xing ming:_ming];
    //    person.friends = [[NSMutableArray alloc] initWithArray:_friends copyItems:YES];
        person.friends = [_friends mutableCopy];
        return person;
    }
    

    14.将方法响应能力缓存起来的最佳途径是使用“位段”数据类型。这是一项乏人问津的C语言特性。我们可以把结构体中某个字段所占用的二进制位个数设为特定的值。例如:

    struct data {
      unsigned int fieldA : 8;
      unsigned int fieldB : 4;
      unsigned int fieldC : 2;
      unsigned int fieldD : 1;
    };
    

    在结构体中,fieldA位段将占用8个二进制位,fieldB占用4个,fieldC占用两个,fieldD占用1个。这样fieldD可表示0或1两个值。我们可以像fieldD这样,吧委托对象是否实现了协议中的相关方法这一信息缓存起来。次结构体用法如下:

    @interface NetWorkFetcher() {
      struct {
        unsigned int didReceiveData                : 1;
        unsigned int didFailWithError              : 1;
        unsigned int didUpdateProgressTo           : 1;
      } _delegateFlags;
    }
    @end
    

    该结构体含有三个位段,每个位段都与delegate所遵从的协议中某个可选方法相对应,用来缓存委托对象是否能响应特定的选择子。在NetworkFecther类里,可以像下面这样查询并设置结构体中的段位:

    // set flag
    _delegateFlaggs.didReceiveData = 1;
    if (_delegateFlags.didReceiveData) {
        // Yes,flag set
    }
    

    实现缓存功能所用的代码可以写在delegate属性对应的设置方法里:

    - (void) setDelegate:(id<NetworkFetcherDelegate>delegate) {
      _delegate = delegate;
      _delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
       ...
    }
    

    这样的话,每次调用delegate的相关方法之前就不用检测委托对象是否能响应给定的选择子了,而是直接查询结构体里的标志:

      if (_delegateFlags.didUpdateProgressTo) {
        [_delegate networkFetcher:self didUpdateProgressTo:currentProgress];
    }
    

    在相关方法要调用很多次时,指的进行这种优化。
    15.Category执行顺序


    编译顺序
    load顺序 编译顺序 load顺序

    load执行顺序是先执行本类的,再执行分类的,分类的执行顺序由Compile Sources中文件编译顺序决定的,在上面的文件先编译,下面的文件后编译。
    本类和分类有同名方法的,优先调用编译顺序在后面的分类中的方法。
    category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休。
    16.锁和主线程


    锁和主线程

    17.@protocol中定义@property
    默认没有set/get方法,在遵守协议的类中直接调用.xxx会挂的,写一个@syncthesize让编译器帮你实现set/get方法,或者自己定义一个成语变量,手动实现set/get方法。

    //MyProtocal.h
    @class NSString;
    
    @protocol MyProtocal <NSObject>
    @optional
    @property (nonatomic, copy) NSString *name;
    @end
    
    //xxObject.h
    #import "xxObject.h"
    
    @interface TWPersion : NSObject<MyProtocal>
    
    @end
    
    //xxObject.m
    #import "MyProtocal.h"
    @implementation xxObject
    @synthesize name = _name;
    
    - (NSString*)description{
        return [NSString stringWithFormat:@"%@",self.name];
        // return [NSString stringWithFormat:@"%@",_name];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        xxObject *obj = [[xxObject alloc] init];
        obj.name = @"wang";
        NSLog(@"%@",obj.description);
    }
    
    输出结果

    相关文章

      网友评论

        本文标题:Effective OC

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