字符串处理NSString
认识字符串NSString
- NSString是一个Unicode编码、16位字符的字符序列
- NSString被定义为类,引用类型,拷贝时具有引用语义。
- 初始化方法:字面初始化、初始化器、工厂方法
- NSSring拥有恒定性,所有的操作无法更改字符串本身,如果有更改,都是返回新值的形式。
- NSString拥有共享机制,引用计数管理对其有特殊的管理规则
oc的NSString和c语言的区别
- NSString是一个oc对象,OC字符串是一个字符串对象,字符串常量需要用@""包含。
- C语言字符串是一个数据类型,用""包含
- OC中的字符串以Unicode编码,C语言的字符串以字符的ASCII码形式。
- 打印OC字符串用%@,打印C语言字符串用%s
三种初始化方法:
NSString *str1 = @"Hello World!";
//字面常量,加@才是OC的字符串
NSString *str2 = [[NSString alloc]initWithCString:"Hello World!" encoding:NSUTF8StringEncoding];
//用内存分配搭配初始化器
NSString *str3 = [NSString stringWithCString:"Hello World!" encoding:NSUTF8StringEncoding];
//工厂方法,是一个类方法,通过类方法内部返回一个对象
NSString *str4= @"Hello World!";
上面运行结果str1
和str4
的存放地址是一样的(共享机制)。只要用字面常量创立的字符串才会共享。
- 判断相等
isEqualToString
值是否相等 -
str1 == str2
比较指针(引用)是否相等
NSMutableString
- NSMutableString具有可变性,NSString具有恒定性
- NSMutableString不具有共享机制,NSString具有共享机制
- NSMutableString不是在原有内存上直接增长,而是重新分配一个更大或更小的缓存容量存放字符
- NSMutableString是NSString的子类
NSMutableString *mustr1 = [NSMutableString stringWithString: @"Hello,World!"];
//工场方法停止初始化
NSLog(@"mustr1:%p",mustr1);
NSMutableString *mustr2 = [NSMutableString stringWithString: @"Hello,World!"];
NSLog(@"mustr2:%p",mustr2);
NSString *str5=mustr1;
NSLog(@"str5:%@",str5);
[mustr1 appendString:@" Very Good!"];
NSLog(@"str5:%@",str5);
//NSMutableString的改动,使他的父类NSString发作了改动
存在的固有问题:通过mustr1
的改动,使的父类NSString发生了改变(而上面提到NSString具有不变性)
缓存容量与增长
- 字符串初始化后,会分配一个缓存容量capacity,其长度一般大于实际的字符串数量,当然也可以自己给它一个缓存容量
- 当字符串增长时,如果实际需求大于capacity,其capacity会以两倍的方式指数增长,代价是:
- 分配新的堆内存2*capacity
- 将原来堆内存的内容拷贝到新内存,
- 释放原来堆内存
- 最佳实践:估计好capacity,预先分配好一定容量,避免以后capacity的增长
常见操作
- NSString常见操作
- 访问字符串:获取字符串、字符串长度、字面量、大小写转换
- 查询字符串:定位子串、获取子串、是否包含子串、查询字符集
- 其他操作:比较字符串、替换字符串、分解字符串
- NSMutableString:
- 添加字符串
[mustr3 appendString:@"Hello Objective"];
- 删除字符串
- 修改字符串
- 添加字符串
集合类型
数组
- 数组是一个有序的元素序列,支持随机存取。索引从0开始,索引访问越界会抛出运行时异常。注意与C语言数组不同。
NSArray *array1=[NSArray arrayWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
NSArray *array2=[[NSArray alloc] initWithObjects:@"Shanghai",@"Beijing",@"New York",@"Paris", nil];
NSArray *array3=@[@"Shanghai",@"Beijing",@"New York",@"Paris"];
可以通过
count
方法访问数组中元素的个数,返回一个NSInteger
.NSArray被定义为class,引用类型,拷贝时具有引用语义。
-
NSArray的元素必须是对象,即NSObject的子类。
- 如果为基本数值类型,须要用NSNumber 封装为对象类型后,才能放入数组中。
NSInteger number=100; NSNumber *numberObject1= [NSNumber numberWithInteger:number ];
- 如果为C语言结构类型,须要用NSValue 封装为对象类型后,才能放入数组中。
NSValue *pointObject = [NSValue value:&point withObjCType:@encode(Point)];
- 数组元素可以是不同对象类型,可能会有类型不安全。
NSArray具有常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
NSArray内存模型
数组遍历
- 最快 for in 直接访问内存,优化索引检查,比for循环快5-10倍
- 较慢 NSEnumeration遍历迭代的方法
NSEnumerator *enumerator = [array5 objectEnumerator];
BLNPoint* item;
while (item = [enumerator nextObject]){
item.x++;
item.y++;
}
- for循环最慢
数组查找的方法
-
indexOfObject
查找是否存在“值相等”的对象(类型需要实现isEqual)。NSUInteger index1=[array5 indexOfObject:target];
-
indexOfObjectIdenticalTo
查找是否存在“指针想等”的对象。NSUInteger index2=[array5 indexOfObjectIdenticalTo:p3];
objectAtIndex:
方法访问数组中的单个元素,返回给定索引处的单个元素。lastObject:
方法返回数组的最后一个元素。
数组排序
不改变原数组(常量性),返回新数组。
NSArray* sortArray1=[array1 sortedArrayUsingSelector:@selector(compare:)];
使用NSMutableArray
- NSMutableArray支持更改数组长度和元素指针,为NSArray的子类。
- capacity与NSMuatbalString一样
- 估计好capacity预先分配一定容量,避免以后增长
- 尽量避免使用
insertObject:atIndex:
和removeObjectAtIndex:
等操作,因为会改变数组元素序列,涉及大量内存拷贝操作,代价高。
set
- NSSet 是一个无序的集合,其存储的对象不能重复。
- 常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
- 创建NSMutableSet时用initWithCapacity提前设置capacity。
Dictionary字典类型
NSDictionary是一个存储key-value的无序集合,key唯一,value可重复。
NSDictionary被定义为class,引用类型,拷贝时具有引用意义。
常量集合 NSDictionary,可变集合: NSMutableDictionary:
常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
创建NSMutableDictionary 时用initWithCapacity 提前设置capacity
支持Fast Enumeration和NSEnumerator遍历,前者较快。
-
排序方法
- sortedArrayUsingSortDescriptors:
- sortedArrayUsingFunction:context:
- sortedArrayUsingSelector:
其他类型
- NSMumber类是用来封装基本数据类型。
- 装箱(boxing)是一个基本类型的数据封装成对象的过程
- 开箱(unboxing)从对象中提取基本类型的数据
+(NSNumber *) numberWithChar:(char) value;
+(NSNumber *) numberWithInt:(int) value;
+(NSNumber *) numberWithFloat:(float) value;
+(NSNumber *) numberWithBool:(BOOL) value;
//可以通过下面的实例方法重新获得基本类型数据:
-(char) charValue;
-(int) intValue;
-(float) floatValue;
-(BOOL) boolValue;
-(NSString *) stringValue;
- NSValue可以封装任意值,可以使用NSValue将结构体放入NSArray和NSDictionary中。
+(NSValue *) valueWithBytes: (const void *) value objCType:(const char *) type;
自动引用计数ARC
ARC
- 自动引用计数(Automatic Reference Counting)是Objective-C默认的内存管理机制,其针对堆上的对象,由编译器自动生成操作引用计数的指令(retain或release),来管理对象的创建与释放。
- 哪些对象受ARC管理:
- OC对象指针
- Block指针
- 使用attribute((NSObject))定义的typedef
- 哪些对象不受ARC管理:
- 值类型(简单值类型,C语言struct)
- 使用其他方式分配的堆对象(如使用malloc分配)
- 非内存资源
引用计数管理
- 新创建(使用alloc,new,copy等)一个引用类型对象,引用计数为1
BLNPoint *p1 = [[BLNPoint alloc]init];
BLNRectangle *rect = [[BLNRectangle alloc]init]; - 对象引用计数增1——retain操作:
- 将对象引用赋值给其他变量或常量。
BLNPoint *p2 = p1;
- 将对象引用赋值给其他属性或实例变量。
rect.center = p1;
- 将对象传递给函数参数,或者返回值。
draw(p1);
- 将对象加入集合中。
array=[[NSMutableArray alloc]initWithCapacity:10];
[array addObject:p1];
- 将对象引用赋值给其他变量或常量。
- 对象引用计数减1——release操作:
- 将局部变量或全局变量赋值为nil或其他值。
p1 = nil;
p2 = nil;
- 将属性赋值为nil或其他值。
rect.center = nil;
- 实例属性所在的对象被释放。
- 参数或局部变量离开函数。
- 将对象从集合中删除。
[array removeObjectAtIndex:0];
- 将局部变量或全局变量赋值为nil或其他值。
- 引用计数变为0时,内存自动被释放
自动释放池(Autorelease Pool)
release会导致对象立即释放。如果频繁对对象进行release,可能会造成琐碎的内存管理负担。autorelease可以将release的调用延迟到自动释放池被释放时。
推荐使用自动释放池(AutoreleasePool)Block,当其结束时,所有接受autorelease消息的对象将会被立即释放(即发送release消息)。
AppKit和UIKit框架在处理每一次事件循环迭代时,都会将其放入一个Autorelease Pool中。大多数情况,无需程序员干预。
-
什么时候需要手工管理Autorelease Pool
- 编写的程序不基于UI框架,如命令行程序。
- 在循环中创建大量临时对象,需要更早地释放,避免临时对象聚集导致内存峰值过大。
- 在主线程之外创建新的线程,在新线程开始执行处,需要创建自己的Autorelease Pool.
- 可以嵌套使用Autorelease Pool.
类型合同:协议
认识协议 Protocol
- 协议:类型的合同约定,只描述外部接口,不提供具体实现。
- 协议可以包含以下成员:
- 属性
- 实例方法
- 类方法
- 初始化器-不常用
- 析构器-不常用
@protocol Drawable
@property NSInteger x;
@property NSInteger y;
-(void)draw;
+(void)createShape;
@optional
-(void)moveToX:(NSInteger)x withY:(NSInteger)y;
@end
- 协议中无法包含实例变量成员
- 协议中定义的属性本质上是访问器方法,编译器不会合成实例变量。
协议关键字
-
@required:
表明其后所有的方法是实现该协议的必须方法。协议的默认行为,如果没有指定@required ,协议中声明的所有方法都默认是必须实现的。 -
@optional:
表明实现类时可以选择性实现该方法。实现了该协议的类可以选择不实现任何在@optional后所有声明的方法。
使用协议
- 一个类遵守协议,需要实现协议约定的的所有@required成员
@interface BLNPoint : NSObject
@implementation BLNPoint
-(void)draw
{
NSLog(@"%@",self);
}
+(void)createShape{
NSLog(@"Create a Shape");
}
-(void)moveToX:(NSInteger)x withY:(NSInteger)y
{
self.x = x;
self.y = y;
}
@end
- 协议中的属性须在实现类的.h文件中声明(编译器合成实例变量需要)。
@property NSInteger x;
@property NSInteger y;
- 注意编译警告️:
- 遵守协议后却没有实现必选协议方法时,会出现警告提示。
- 协议类型变量被赋值非协议类型对象时,会出现警告提示。
- 协议本质上是一种类型,可以作为声明类型,但不能创建实例。
- 检查协议类型
-->使用conformsToProtocol:检查对象是否实现了协议。
void process2(id obj){
if ([obj conformsToProtocol:@protocol(AProtocol) ]) {
[obj methodA];
}
}
更多协议形式
- 协议继承
- 一个协议可以继承一个或多个协议
- 实现子协议的类型,也必须实现父协议中约定的成员
- 协议组合
- 可以使用protocol来组合多个协议
- 实现组合协议的类型,必须实现组合协议中的每一个协议
- 可选协议
- 协议的 某些成员可以定义为optional,不必实现
- 常用协议
- NSObject:包含对象的常用操作,想等、字符串表示、哈希
- NSCopying:支持复制的类型必须遵守该协议
- NSFastEnumeration:实现快速枚举 for-in的类型采用
- NSCoding协议:支持将对象图进行编码/解码以支持对象序列化
类别与扩展
类别Category
- 类别支持在没有源代码的情况下,基于某些特定的场合,为一个类增加功能。
- 可以添加
- 类方法
- 实例方法
- 重写基类方法
- 不能添加
- 属性
- 实例变量
- 已存在的同名方法
@interface BLNPoint(Drawing)
-(void)draw;
-(void)setWeight:(NSInteger)weight;
-(NSInteger)weight;
@end
- 命名规范
- 文件名:类名+扩展方法,如 NSString+Drawing.h/.m
使用场景
- 适合在没有源代码的情况下,向已经封装的类中添加方法。
@interface NSString(Drawing)
- 为一个类在某些特殊场景下增加功能
@interface BLNPoint(Drawing)
-(void)draw;
-(void)setWeight:(NSInteger)weight;
-(NSInteger)weight;
@end
- 对于复杂的大型文件分割实现。
- 添加类别
- 自己创建的类
- 系统的类
- 第三方库
扩展extension
- 扩展支持在编译时,有类的源代码的前提下,向类添加功能。可以将扩展看做匿名的类别。
- 接口定义在.m文件中@implementation前声明,实现代码仍然在@implementation内实现。
@interface Circle ()
{
NSString * _name;
}
@property (readwrite )NSInteger radius;//修改读写属性
@property NSInteger center;//添加属性
-(float)getDiameter;//实例方法
+(void)process:(Circle*) circle;//类方法
@end
- 扩展支持添加以下成员:
- 添加属性。
- 添加实例成员。
- 添加类方法。
- 添加实例方法。
- 改写属性的读写属性。
使用扩展
- 扩展实现的成员都只能在.m实现文件内部访问,在类外不可以直接访问。
- 扩展的主要用途在于信息隐藏,隐藏一些外部无需访问、而内部实现又需要使用的属性、方法:
- 类的主接口主要用于“对类外公开”的接口。
- 类的扩展接口用于“对类内可见”的接口。
OC泛型
泛型是程序设计语言的一种特性,他主要是为了限制类型的,比如OC中的数组,你可以限制他里面装的是NSString类型。
-
泛型的基本格式
- 泛型声明格式:在声明类的时候,在类型后面<泛型名称>
- 泛型定义格式:放在限制的类型后面<类型>
-
泛型的好处
- 提高程序员开发规范,让程序员一眼就可以看出该使用什么类型
- 限制类型,不允许装入其它的类型
- 可以使用点语法