Effective-Objective 2.0整理

198 阅读5分钟

哨兵查找法

题目:在array中,查找item,返回index

正常写法中的判断条件有index&item

使用哨兵,减少index的判断

{
    index = arr.count - 1
    originFirst = arr[0]
    arr[0] = item
    while(arr[index] != item) { 
        index—; 
    }
    arr[0] = originFirst
    return index > 0 ? index : -1
}

当数量达到万级别时,执行时间会出现差异。

Objective-C

  • 采用消息结构,非函数调用。
    • 消息结构是指运行时所执行的代码,由运行环境决定。
    • 函数调用是指运行时所执行的代码,由编译器决定。
  • 运行期会检查对象类型。
  • 运行期组件,本质是动态库,包含了对象的数据结构和函数。
  • 对象所占内存,分配在堆上;变量为指针,分配在栈上。

??为什么要设计成运行时语言?

响应多态运行时多态的基础。

多态是指不同的对象按照自己的方式来响应相同的消息

头文件

在类的头文件中,尽量少引用文件。

可以采用@class **向前声明。

使用#import,会引用到许多用不到的内容,增加编译时间。优点是可以解决相互引用的问题。

多用字面量语法,少用与之等价的方法

类型常量与#define

多使用类型常量,少用#define

类型常量会进行类型的检查。

使用#define可能会出现多次被替换的场景,而且没有作用域的概念。

定义类型常量时,经常使用static+const

static意味着该变量仅在定义该变量的编译单元中可见。

如果没有static,编译器会默认创建一个外部符号,导致变量在其他编译单元中可见。

使用static+const,保证编译器不会创建外部符号,而是会像#define一样,仅做替换。

如何将变量外部可见?

.h:extern NSString *const a;
.m:NSString *const a = @"aa";

枚举

使用枚举表示状态、选项、状态码。

enum AA {};
enum AA a = AA1;

typedef enum AA = A;
A a = AA1;

新式枚举:可以指明类型,如果不指定,编译器不清楚数据类型的大小,无法确定分配空间。

enum AA : NSInteger {};

通常我们在定义枚举时,使用了系统提供的辅助宏

typedef NS_ENUM(NSUInteger, AA) {};
typedef NS_OPTIONS(NSUInteger, BB) {};

辅助宏用于判断是否采用新式枚举。

什么时候使用NS_OPTIONS

凡是需要按位操作来组合的枚举,都要使用NS_OPTIONS

因为代码在是否C++模式下编译,处理是不一样的。

在使用或|处理枚举类型时,C++认为枚举结果类型应该是底层的数据类型NSUInteger,而且不允许隐式转换成枚举类型。

eg:AA1 | AA2

得到的结果类型是NSUInteger,而不是AA3。NS_OPTIONS会区分C++模式编译和 非C++模式编译,在非C++编译模式下=NS_ENUM,而在C++模式下,则会进行特殊处理,保证结果类型。

在switch中,如果是处理枚举,最好不要使用default,这样在后期加入新枚举值时,可以提示开发人员进行处理。

属性

特质:

  • 原子性

    atomic/nonatomic,默认atomic,是否使用同步锁机制。并不是完全的线程安全,如果想保证线程安全,需要更深层次的锁。iOS上一般使用nonatomic,mac os上一般使用atomic。

  • 读写权限

  • 内存管理 strong、assign、weak、copy

使用属性,编译器会自动编写读写该属性的方法,这个过程叫做自动合成

@dynamic:阻止自动合成

@synthesize:指定实例变量的名字

读取实例变量时,一般采用直接访问形式,设置时通过属性。优点:

  • 不经过方法派发,速度快
  • 绕过了为属性定义的内存管理
  • 不会触发KVO

初始化方法 及dealloc时,可以通过实例变量惰性初始化时,需要通过属性

Q:为什么atomic不是完全的线程安全? A:atomic不能保证对象多线程的安全,它只是能保证你访问的时候给你返回一个完好无损的Value而已。只是保证了setter和getter方法的线程安全,其他操作不保证。

对象等同性

对象相等,hash值相同;hash相同,对象不一定相等。

isEqual+hash。

编写hash时,应该选用计算速度快且hash值碰撞几率低的算法。

关联对象

只有在其他办法不行时,再使用关联对象,主要缺点是不易查找问题。

设置关联对象时,key通常使用 静态全局变量。

关于关联对象,见文章关联对象

对象

类型为id的对象,编译器假定它能响应所有消息。

运行期检查对象类型,又叫做 类型信息查询(内省)

不要直接比较对象所属的类,应该通过类型信息查询。因为某些对象可能实现了消息转发功能,eg:代理对象。

每个oc对象实例,其实是指向内存数据的指针,所以带*。

isMemberOfClass:是否是某个特定类的实例。

isKindOfClass:是否是某个类或其派生类的实例。

缓存

构建缓存时选用NSCache 而非NSDictionary

当系统资源耗尽时,NSCache可以自动删减缓存。

如果是普通的字典,则要自己编写手工删减缓存代码。

NSCache还会先删减最久未使用的对象

NSCache是线程安全的。

内存管理

见文章小整内存管理

Block

见文章开发必备-Block

GCD

见文章开发必备-多线程