买了一些书在看,总结了一些<编写高质量代码-改善objective-c程序的61个建议>中的内容
基础篇
在头文件中尽量减少其他头文件的引用
在面向对象开发语言中,对于类的描述,通常划分为头文件和源文件,头文件用于描述类的声明和可公开部分,而源文件用于描述类的方法或函数的具体实现,这也体现了面向对象语言的"封闭性"和"高内聚低耦合"的特性.
在oop(面向对象编程)中有两个技术用于描述类与类或对象之间的关系:一个是继承;另一个是复合.在objective-c中,当一个类需要引用另一个类,即建立复合关系时,需要在类的头文件中,通过"#import"修饰符俩建立被引用类的指针.
使用关键字@class:假如有100个头文件,都用"#import"引用了同一个头文件,那么头文件有变化时,后面所有引用它的类都需要重新编译,这将耗费大量的时间,而使用@class则不会.同时使用@class可以避免"类循环依赖"出现的编译错误.当然还是尽量避免"类循环依赖"的出现.
在一个头文件中包含多个类的生命定义时,要与该头文件生命的多个类建立复合关系,还是要采用关键字"#import"
这些的核心目的是"降低类与类之间的耦合度",在自己设计类的时候,除了"#import"和"#class"之外,有没有一种更好的方式?
有的,一种是通过使用模块方式与多类建立复合关系;另一种是通过使用"协议"的方式来实现.
尽量使用模块方式与多类建立复合关系
"#import"实质上是objective-c为了避免重复引用可能带来的编译错误(比如重复的引用)而加入的,保证每个头文件只会被引用一次.
"#import"的实现是通过对#ifndef一个标志进行判断,然后在引入#define这个标志,来避免重复引用的.
"import"其根本上就是简单的复制,粘贴,将目标.h文件中的内容一字不落复制到当前文件中,后者可以避免多次的重复引用.
在编写objective-c代码中,#import UIKit/UIKit.h引入这个库其实意味着每个文件编译都超过11000行代码.
"利用模块(modules)来解决历史问题-事半功倍"
模块功能,其应用不仅仅表现于编译的速度加快,同事在链接框架等方面也非常好用.
启动模块功能后,编译器会隐式地把所有的#import都转换成@import.
语法有一点小小的改动,用"@import"代替"#import"
技术上,自己不需要吧所有的#import都换成@import,因为编译器会隐式地转换它们,但是还是建议尽可能地用新的语法来编写代码.
尽量使用const,enum来替换预处理#define
"#define"定义了一个宏,在编译开始之前就会被替换.const只是对变量进行修饰,当试图去修改该变量时,编译器会报错.在一些场合你只能用"define",而不能用const.理论上来说,const不仅在运行时需要占用空间,而且还需要一个内存的引用,但从时间上来说,这是无关紧要的,编译器可能会对其进项优化.
"#define"在理论上来说更高效,但在现代编译器上,他们可能没什么不同.
"#define"会更安全,因为当师徒赋值给它是,总会出现一个编译器错误.
因此,相对字符串字面量或数字,更推荐使用常量.应使用static方式声明常量,而非使用#define的方式来定义宏.
对于整形类型,代替"#define"比较好的方式是使用enum,在使用enum时,推荐使用最新的NS_ENUM和NS_OPTIONS宏.
要点: 尽量避免使用"#define"预处理命令,"#define"预处理命令不包含任何的类型信息,仅仅是在编译前做替换操作.他们在重复定义时不会发出警告,容易在整个程序中产生不一致的值.
优先使用对象字面量语法而非等效方法
使用对象字面量语法来创建字符串,数字,数组和字典等,比使用以前的常规对象创建方法语法更为精简,同时可以避免一些常见的陷阱.
对象字面量语法特性是完全向下兼容的.
使用对象字面量语法时,容器类的不可是nil,否则运行时将会抛出异常.
在创建可变字典,无法判断值是否为nil时,可以用*dict setObject:<#(nonnull id)#> forKey:<#(nonnull id)#>这个方法给字典添加键值队,注意,在使用dict setValue:<#(nullable id)#> forKey:<#(nonnull NSString *)#>*时如果值为nil还是会抛出异常.
处理隐藏的返回类型,优先选择实例类型而非id
实例类型(instancetype)不仅可以作为方法返回的实例的类型,且能用这个实例类型来作为想编译器的提示,提示方法返回的类型将是方法所属的类的实例.
使用instancetype可避免隐式转换id而造成的欺骗性编译无误通过的现象,防止程序正式运行时出现崩溃现象,可以大大改善objective-c代码的类型安全.
在某一个特定区域,instancetype可以代替id,但并不是所有.
@interface myObject : NSObject
+(id)factoryMethodB;
@end
@implementation myObject
+(id)factoryMethodB {
retur [[[self class]alloc]init];
}
@end
myObject *ob = [[myObject factoryMethodB]count];//myobject类并没有count方法,但是编译器不会报错,因为id类型的对象可以作为任何类,并且其他一些类中有count方法.如果换成instancetype则会发出警告
高度警惕空指针和野指针的袭击
没有存储任何内存地址的指针就称为空指针(NULL指针).空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0.也就是说,一个指针变量分配一个NULL值的情况下,没有确切的地址被分配.
野指针不是NULL指针,而是指向"垃圾"内存(不可用内存)的指针.野指针是非常危险的.利用野指针发消息时很危险的,会报错.
利用空指针发消息时没有任何问题的,也就是说代码是没有错误的.
清楚常量字符串和一般字符串的区别
NSString *string1 = @"hello";
NSString *string2 = @"hello";
if (string1 == string2)
{
NSLog(@"same");
}
对字符串常量string1和string2的地址值进行比较,就会发现二者竟然是相等的,产生这样的结果要归咎于编译器优化的结果.
由于常量会占用一块特殊的代码段,加载到内存时会映射到一块常量存储区,以加快访问速度.编译器在编译时发现string1和string2的内容是相同的常量字符串,会把他们都指向一个相同的区域,而不是再开辟一块额外的空间.因此,他们是相同的地址值.
如果使用一个常量字符串来初始化另一个字符串,另一个字符串会直接通过地址赋值为常量字符串,alloc的内存也会立即释放.
在访问集合时要优先考虑使用快速枚举
快速;枚举是一种语言中用于快速安全的枚举一个集合的表达式.
for...in表达式,就是一种快速枚举表达式.
快速枚举是访问数组中的对象的一种比较快的方法.
大部分还是书中的内容,自己做了一些小的总结,有不对的地方还希望大家可以指出