字符串处理
认识字符串NSString
- NSString 是一个Unicode编码、16位字符的字符序列
- NSString 被定义为类,引用类型,拷贝时具有引用语义
- 初始化方法:字面量初始化、初始化器、工厂方法。
- NSString拥有恒定性,所有的操作无法更改字符串本身,如有更改,都是返回新值的形式
- NSString 拥有共享机制,引用计数管理对其有特殊的管理规则。
%p表示地址
NSMutalbeString
- NSMutableString具有可变性,NSString具有恒定性。
- NSMutableString是NSString的子类。
- NSMutableString不具有共享机制,NSString具有共享机制。
- NSMutableString并不是在原有内存上直接增长,而是重新分配一个更大或更小的缓存容量存放字符。
NSString 常用操作
- NSString:
- 访问字符串:获取字符串字符、字符串长度、字面量、大小写转换。
- 查询字符串:定位子串、是否包含子串、查询字符集
- 其他操作:比较字符串、替换字符串、分解字符串。
- NSMutableString
- 添加字符串
- 删除字符串
- 修改字符串
集合类型
认识数组
- 数组是一个有序的元素序列,支持随机存取。索引从0开始,索引访问越界会抛出运行时异常。注意与C语言数组不同。
- NSArray的元素定义为class,引用类型,拷贝时具有引用语义
- NSArray的元素必须是对象,即NSObject的子类
- 如为基本数值类型,须用NSNumber封装为对象类型后,才能放入数组中。
- 如为C语言结构类型,须用NSValue封装为对象类型后,才能放入数组中。
- 数组元素可以是不同对象类型,可能会有类型不安全。
- NSArray具有常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
常用操作
- 数组遍历
- 最快 -- Fast Enumeration,直接访问内存,优化索引检查,快5-10倍
- 较慢 -- NSEnumerator遍历:索引检查+动态消息调用
- 最慢 -- For 循环访问:索引检查+动态消息调用
- 数组查找
- indexOfObject 查找是否存在“值相等”的对象(类型需要实现isEqual)
- indexOfObjectIdenticalTo查找是否存在“指针相等”的对象
- 数组排序
*不改变原数组(常量性),返回新数组。
使用NSMutableArray
- NSMutableArray支持更改数组长度和元素指针。为NSArray子类。
- NSMutableArray初始化后,会分配一个缓存容量capacity,一般大于实际元素数量,当元素长度增加时,如实际需求大于capacity,其capacity会以近似二倍的方式指数增长。伴随代价:
- 分配新的堆内存 2*capacity
- 将原来堆内存上的元素拷贝到新内存
- 释放原来的堆内存
- 最佳实践:估计好capacity,预先分配一定容量,避免以后增长
- 尽量避免使用insertObject:atIndex;和removeObjectAtIndex;等操作,因为会改变数组元素序列,涉及大量内存拷贝操作,代价高。(假如我有这种需要呢?)
认识Set
NSSet是一个无序的集合,其存储的对象不能重复
NSSet被定义为class,引用类型,拷贝时具有引用语义
-
常量集合NSSet,可变集合:NSMutableSet:
- 常量性:长度和元素指针都不能改变。但指针指向的对象内部可以更改。
- 创建NSMutableSet时用initWithCapacity提前设置capacity
支持Fast Enumeration和NSEnumerator遍历,前者较快。
认识Dictionary
- NSDictionary是一个存储key-value的无序集合,key唯一,value可重复
- NSDictionary被定义为class,引用类型,拷贝时具有引用语义。
- 常量集合NSDictionary,可编辑和:NSMutableDictionary:
- 常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
- 创建NSMutableDictionary时用initWithCapacity提前设置capacity
- 支持Fast Enumeration和NSEnumerator遍历,前者较快。
自动技术引用ARC
- 自动引用计数(Automatic Reference Counting)是Objective-C默认的内存管理机制,其针对堆上的对象,由编译器自动生成操作引用计数的指令(retain或release),来管理对象的创建与释放。
- 那些对象受ARC管理:
- OC对象指针
- Block指针
- 使用attribute((NSObject))定义的typedef
- 那些对象不受ARC管理:
- 值类型(简单值类型,c语言struct)
- 使用其他方式分配的堆对象(如使用malloc分配)
- 非内存资源
引用计数管理
- 新创建(使用alloc, new, copy等)一个引用类型对象,引用计数为1
- 对象引用计数增1--retain操作
- 将对象引用赋值给其他变量或常量
- 将对象引用赋值给其他属性或实例变量
- 将对象传递给函数参数,或者返回值
- 将对象加入集合中
- 对象引用计数减1--release操作
- 将局部变量或全局变量赋值为nil或其他值
- 将属性赋值为nil或其他值
- 实例属性所在的对象被释放
- 参数或局部变量离开函数
- 将对象从集合中删除
- 引用计数变为0时,内存自动被释放。
自动释放池(Autorelease Pool)
- release会导致对象立即释放。如果频繁对对象进行release,可能会造成琐碎的内存管理负担。autorelease可以将release的调用延迟到自动释放池被释放时。
- 推荐使用自动释放池(Autorelease Pool)Block,当其结束时,所有接受autorelease消息的对象就会被立即释放(即发送release消息)
- AppKit和UIKit框架在处理每一次事件循环迭代时,都会将其放入一个Autorelease Pool中。大多数情况,无需程序员干预。
什么时候需要手工管理Autorelease Pool
- 编写的程序不基于UI框架,如命令行程序。
- 在循环中创建大量临时对象,需要更早地释放,避免临时对象聚集导致内存峰值过大。
- 在主线程之外创建新的线程,在线程开始执行出,需要创建自己的Autorelease Pool
- 可以签到使用Autorelease Pool
类型合同:协议
认识协议Protocol
- 协议:类型的合同约定,只描述外部接口,不提供具体实现
- 协议可以包含以下成员:
- 属性
- 实例方法
- 类方法
- 初始化器-不常用
- 析构器 - 不常用
- 协议中无法包含实例变量成员
- 协议中定义的属性本质上是访问器方法,编译器不会合成实例变量。
使用协议
- 一个类遵守协议,需要实现协议约定的所有@required成员
- 协议中的属性项目须在实现类的.h文件中声明(编译器合成实例变量需要)。
- 注意编译警告信息:
- 遵守协议后却没有实现必选协议方法时,会出现警告提示。
- 协议类型变量被赋值非协议类型对象时,会出现警告提示。
- 协议本质上是一种类型,可以作为声明类型,但不能创建实例。
- 检查协议类型
- 使用conformsToProtocol:检查对象是否实现了协议
更多协议形式
- 协议继承
- 一个协议可以继承一个或多个协议
- 实现子协议的类型,也必须实现父协议中约定的成员
- 协议组合
- 可以使用protocol<A,B,...>来组合多个协议
- 实现组合协议的类型,必须实现组合协议中的每一个协议
- 可选协议
- 协议的某些成员可以定义为optional,不必实现。
了解常用协议
- NSObject:包含对象的常用操作,相等、字符串表示、哈希
- NSCopying:支持赋值的类型必须遵守该协议
- NSMutableCopying:在NSCopying协议的基础上,支持赋值数据的可变性。
- NSFastEnumeration:实现快速枚举for-in的类型采用。
- NSCoding协议:支持将对线图进行编码/解码以支持对象序列化。
类别 Category
类别支持在没有源代码的情况下,基于某些特定的场合,为一个类增加功能。
-
可以添加
- 类方法
- 实例方法
- 重写基类方法
-
不能添加
- 属性
- 实例变量 (相当于更改类的内存模型)
- 已存在的同名方法
-
命名规范
- 文件名:类名+扩展方法,如:NSString+Drawing.h/.m
扩展 Extension
- 扩展支持在编译时、有类的源代码的前提下,向类添加功能。可以将扩展看做匿名的类别。
- 接口定义在.m文件中@implementation钱声明,实现代码仍然在@implementation内实现。
- 扩展支持添加一下成员:
- 添加属性
- 添加实例成员
- 添加类方法
- 添加实例方法
- 改写属性的读写属性
使用扩展
- 扩展实现的成员都只能在.m实现文件内部访问,在类外不可以直接访问。
- 扩展的主要用途在于信息隐藏,隐藏一些外部无需访问、而内部实现又需要使用的属性、方法:
- 类的主接口主要用于“对类外公开”的接口
- 类的扩展接口用于“对类内可见”的接口。