美文网首页程序员
编写高质量iOS与OS X代码的52个有效方法

编写高质量iOS与OS X代码的52个有效方法

作者: taobingzhi | 来源:发表于2017-09-24 11:27 被阅读0次

    扯会淡

           这周项目有点忙,又要新版本的提测,又要老版本适配iOS 11,确实没有什么成段的时间可以静下心来好好看看书,只能周日的早上迎着”朝阳“看看书(其实是阴天,冷死,杭州这几天天天下雨,I hate rainy days),最近可能会对于项目中适配iOS 11的东西,写一篇文章(看心情吧,哈哈)。

           今天就先对《Effective Objective-C 2.0》的前5条做一个记录和总结。话说这本书好像有很多人推荐,看了一会觉得确实对于代码的细节会有一个深刻的反思,我觉得是本可以细细咀嚼的书。

    第一条:了解Objective-C语言的起源

           OC是基于C编写的,这个应该大家都知道,所以OC支持c,c++的混编,我个人觉得一个真正厉害的OC程序员,应该对于三者混编应该有自己独到的理解(轻喷)。但是OC跟c++,Java等面向对象的语言有一个很大的不同,就是消息结构机制,而不是函数调用机制。

             //Message (OC):

             Object *obj = [Object new];

             [obj performWith:parameter and:parameter1];

             //Function Calling (c++)

             Object *obje = new Object;

             obj->perform(parameter,parameter1);

           这在程序的编译和执行时有很大的区别,最关键的一点区别是:使用消息结构的语言,在运行的时候,怎么执行是由运行环境决定的,就是在编译的时候,根本不关心接收消息的对象是何类型;而使用函数调用的语言,则是由编译器来决定,在编译的时候就已经明确的知道这个函数是谁来执行。

           OC的重要工作都依赖于OC独有的一个运行期组件(Runtime Component)。运行期组件本质上就是一种与开发者写的代码链接的动态库(dynamic library),其功能就是把开发者的所有代码粘合起来。

           想要理解OC的内存模型以及“引用计数”(reference counting)机制,首先需要明白一个前提:Objective-C语言中的指针是用来指示对象的。就是对象多占内存总是分配在堆中,而指向这个对象的指针是分配在栈中。OC将堆内存管理抽象出来了,不需要用malloc和free来分配或释放对象所占内存。OC运行期环境把这一部分工作抽象为一套内存管理架构,名为“引用计数”。

           在OC代码中,有时会遇到不含*的变量,也就是基本数据类型,它们使用的是可能是“栈空间”。比如我们设置frame,经常用到的CGRect,它是个c结构体,

    struct CGRect {

        CGPoint origin;

        CGSize size;

    };

    typedef struct CGRect CGRect;

           OC的整个系统框架,可以看到很多这种结构体,因为相比创建对象来说,节省了分配及释放堆内存的额外开销。如果只需保存基本数据类型,那么通常使用这种c结构体。

    第二条:在类的头文件中尽量少引入其他头文件

           关于这一点,我想所有的程序员应该多多少少都有体会,如果在类A的头文件中引用类B的头文件,又在类B的头文件中引用类A的头文件,就会造成循环引用。对于这一点OC有一个专门的引用指令#import相比#include指令,虽然不会导致死循环,但这却意味着两个类中有一个类无法被正确编译。

           所以正确的做法应该是向前声明,就是只在头文件中声明有另一个类,并不需要知道另一个类的全部细节。就是在类A的头文件中使用@class B;而在实现文件中正确的引入类B#import “B.h”

           //A.h

           #import <Foundation/Foundation.h>

           @class B;

           @interface A : NSObject

           @property (nonatomic, strong) B *b;

            //...

            @end

             //A.m

             #import "A.h"

             #import "B.h"

             @implementation A

             //...

             @end

           有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把“该类遵循某协议”的这条声明移至“class-continuation分类”中。如果不行的话,就把协议单独放在一个头文件中,然后将其引入。

           ”class-continuation“分类就是.m文件中的@interface那一块。

    第三条:多用字面量语法,少用与之等价的方法

             什么是字面量语法?

    NSString *string = @"hello world!";         //字符串字面量

    NSNumber *intNumber = @1;                   //字面数值

    NSNumber *floatNumber = @2.5f;

    NSNumber *doubleNumber = @3.14159;

    NSNumber *boolNumber = @YES;

    NSNumber *charNumber = @'a';

    int i = 5;

    folat f = 5.3f;

    NSArray *array = @[@"cat",@"dog",@"pig"];       //字面量数组

    NSString *dog = array[1];

    NSDictionary *dic = @{@"firstName":@"tao",@"lastName":@"bingzhi",@"age":@25};

           以上都是一些字面量的写法,那么有什么好处呢?很直白的一点是,代码清晰明了易读,没有多余的语法。

           同时在声明数组和字典时,如果有一个元素是nil,这样还会报错,而如果用数组或字典的类初始方法创建的话“arrayWithObjects:”会一次处理各个参数,知道发现nil停止,不至于莫名其妙创建好数组之后发现元素少了。

           当然也有一定的局限性,除了字符串之外,所创建出来的对象必须属于Foundation框架才行。如果自定义了这些类的子类,则无法用字面量语法创建其对象。还有就是字面量创建出来的字符串、数组和字典都是不可变的(immutable)。如果想变成可变的,需要mutaleCopy以下。

    NSMutableArray *mutableArr = [@[@"hh",@"ss",@"tt"] mutableCopy];

    第四条:多用类型常量,少用#define预处理指令

           写代码时常常需要定义常量,那么怎么定义常量才是正确且合适的,常量定义的位置又有什么讲究呢?

           如果我们用#define来预处理一个宽度,可能会这么写

    #define kIconWidth 30;

             这虽然能解决,但是这样定义出来的常量没有类型,代码不易读,可以使用下面这行代码:

    static const CGFloat kIconWidth = 30;

           同时还要注意常量名称,常用的:若常量局限于某实现文件内,则在前面加子母“k”;若常量在类之外可见,则通常以类名为前缀。

           定义常量的位置也很重要,若不打算公开这个常量,则应将其定义在使用该常量的实现文件里。

           这里需要理解一下const和static两个关键字的意思,

           const:如果试图修改由const修饰符所声明的变量,那么编译器就会报错,不能被更新;

           static:就是限制了变量的可用范围,如果是在实现文件中用static声明了一个变量,那么变量的可用域就是该类所生成的目标文件,如果是在方法内用static声明,那么可用域就是这个代码块。

           最主要的一点还是,用这种方法声明常量带有类型信息。

           有的时候你需要公开一个常量,就是无需知道这个常量名背后的意义,只是需要用到这个常量名,例如通知名称的公开定义。这种时候,可以将常量放在“全局符号表“中,此时应该这么声明:

           //.h

           extern NSString *const WLLoginControllerNotification;

           //.m

          NSString *const WLLoginControllerNotification = @"WLLoginControllerNotification"
    ;

          个人更多的还是在pch文件中,直接写一个通知名static const NSString * WLLoginControllerNotification = @“WLLoginControllerNotification”;

    第五条:用枚举表示状态、选项、状态码

           在日常开发中,枚举是很好用的一个东西,可以用来表示状态,传递给方法的选项以及状态码等值,需要主要的是,新的C++11标准修订了枚举的一项改动:可以指明用何种“底层数据类型”来保存枚举类型的变量。这样做的好处是可以向前声明枚举变量,编译器在用到枚举的时候,就知道该给枚举变量分配多少内存空间了。

           如果把传递给某个方法的选项表示为枚举类型,而多个选项又可以同时用,那么就将各选项值定义为2的幂,以便通过换位或操作将其组合起来。

           这里要注意一点:如果想要通过换位或操作的话,定义枚举是就不能用NS_ENUM来定义,而需要使用另一种方式定义,NS_OPTIONS,以便省去类型转换过程。

           最后一个小的tip,在处理枚举类型的switch语句中不要事先default分支,这样的话,加入新枚举之后,编译器会提示开发者:switch语句并未处理所有枚举。

    总结

           今天状态不好,写的时候一直是很赶的情绪,不知道为什么,就没有静下心,很急,很抱歉。可能起太早了,天气又阴沉沉的,还没吃早饭,情绪不太对。sorry sorry。

           最后的最后,还是要说,换季了,大家不要感冒,出门上班可以带件外套。希望大家身体健康,吃嘛嘛香。


    Better Late Than Never!
    努力是为了当机会来临时不会错失机会。

                     共勉!

    相关文章

      网友评论

        本文标题:编写高质量iOS与OS X代码的52个有效方法

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