@字符串处理
NSString是一个Unicode编码,16位的字符序列,是类,引用类型。
初始化方法有字面常量初始化,初始化器与工厂方法:
NSString* p=@“Geek”,
NSString* p1=[[NSString alloc]initwithstring:@"Love"];
NSString * p3=[NSString Stringwithstring:@"Love"];
NSString具有恒定性和共享机制的特点。
所谓恒定性是指NSString的操作无法改变字符串内容,如有更改只能以返回新值得方式(这点与NSMutablestring不同)。
共享机制是指如果两个字符串内容相同,如p2,p3,实际上它们是两个指针指向同一个堆上的数据。这里可以看下NSString的内存模型,具体如下:
下面继续说明一下NSMutablestring。NSmutablestring是NSString的子类,是可变字符串,也就是在加入内容后仍可进行增删改减。
NSMutablestring初始化三种方法与NSString类似,不过这里需要说明NSMutablestring在初始化时会给一个比一般字符串大的capacity,超过时会以两倍的方式增长,并将原来堆上内容复制到新的堆上,最后释放原来的内存。为避免这样的情况,最后在初始化时用stringwithcapacity这个方法预先给一个足够的容量,示例如下:
NSMutablestring * p4=[[NSMutablestring alloc]initwithcapacity:10];
接着再来看看NSString与NSMutablestring的一些操作。
大小写转换:uppercaseString,lowercaseString,首字母大写:capitalizedString,具体使用如下:
[p1 uppercaseString];[p2 lowercaseString];[p1 capitalizedString];
字符串的比较,有isEqualTo与compare的方法,前者是比较指针是否相同,后者用来比较内容是否相同,示例如下:
BOOL flag=[p1 isEqualToString:p2];
提取子字符串的方式如下:
1、substringFromIndex:x//从索引x开始到结束,包含索引x指向的值(索引的理解有点像C语言中数组的下标)
2、substringToIndex:x//从开始到索引x之前结束,不包含索引x指向的值
3、substringWithRange
@集合类型
集合类型大致分为:数组(Array),集合(Set)与字典(Dictionary),示例如下:
可以看出,数组是一个有序的元素序列,索引从0开始,索引访问越界运行时会抛出异常;而集合是一个无序的集合,其存储对象不能复制;字典呢也是无序集合,它存储key-value,其中关键字key唯一,对应的value则可以重复。
数组的元素必须是对象(NSObject的子类),其定义方法有以下几种:
NSArray * array1=[[NSArray alloc]initWithObject:@"one",@"two",@"three",nil];//初始化器赋值;
NSArray * array2=[NSArray arrayWithObject:@"Paris",@"Beijing",nil];//工厂方法赋值
NSArray * array3=@[@"Lakers",@"Celitics",nil]; //直接字面赋值
需要注意的,以nil结尾(与C语言中/0结尾一个意思),表示一个结束符而已。
同样,集合与字典的初始化也有如上几种方法。
之前说过,NSArray的元素必须是对象,当遇到基本数值或是C语言结构,则需要用方法将他们封装成对象在作为数组元素,具体示例如下:
int intValue = 100;
NSNumber *intNumber = [NSNumber alloc numberWithInt:intValue]; //用NSNumber将基本数值封装成一个对象
CGRect rect1 = {10, 10, 200, 200};
NSValue *aRect1 = [NSValue valueWithCGRect:rect1]; //利用NSValue将矩形结构封装成对象
同样还可以利用NSNull封装nil成对象。
由上面可以想到,数组元素可以是不同对象类别,可能有类型不安全。
NSArray具有常量性,长度和指针均不能更改,但是指针指向的对象内部可以更改,其内存模型如下:
相对于常量数组NSArray,其有个子类NSMutablearray(同样NSSet与NSDictionary也有相对应的变量集合NSMutableset和NSMutabledictionary)。
NSMutablearray可以变化指针和数组长度。可以采用addobject,insertObjectAtIndex,removeobjectatindex等命令进行增、擦、删等操作。
对于NSMutablearray有几点需要注意:
1.预先预估好其容量(用工厂方法arrayWithCapacity),因为动态数组初始化时会分配一个缓存容量capacity,当长度增长时,capacity会以近似2倍增长,并将内容拷贝到新内存,释放原内存。反复的话,内存代价大,所以在开始前应估计好容量,预先分配一定容量。
2.与上面道理一致,应尽量避免用insertObject:atIndex:和removeObjectAtIndex:来改变数组。
此外关于数组,有以下几个内容可以了解。
快速遍历,对于NSArray有以下遍历方法:
for( int i=0; i<count; i++){ NSLog(@"%i-%@", i, [array objectAtIndex:i]); } //for循环
for(id obj in array){ NSLog(@"%@",obj); } //快速遍历
NSEnumerator *enumerator = [array objectEnumerator]; id obj = nil;
while(obj = [enumerator nextObject]){
NSLog(@"obj=%@",obj); //迭代器方法
实际还有Block方法:
[array enumeratorObjectsUsingBlock: ^(id obj, NSUInteger index, BOOL *stop)
{ NSLog(@"%i-%@",index,obj); *stop = YES; }];
各方法使用情况如下:
同样集合与字典可用快速遍历与迭代器来遍历。
indexOfObject(IndenticialTo)可以查找是否有值相等(指针相等)的数组存在。
数组
@ARC(Automatic Reference Counting)自动引用计数
其针对堆上的对象,由编译器自动生成操作引用计数的指令(retain或release),来管理对象的创建与释放。
首先应该了解哪些对象受ARC管理:OC对象指针,Block指针与使用_attribute_((NSObject))定义的typedef。
而值类型,使用如malloc等其他方式分配的堆对象,非内存资源等不受ARC管理。
那么哪些会retain哪些会release呢?具体如下图:
讲到ARC,还需说明下自动释放池Autorelease Pool。
release会导致对象立即释放。如果频繁对对象进行release,比较麻烦。Autorelease pool可以将release的调用延迟到自动释放池后。
你可以在需要的线程上在开始执行处创建自己的Autorelease来实现手动释放,同样Autorelease可以嵌套使用。
下面来看看自动释放池的示意图:
@协议Protocol
定义:协议,类型的合同约定,只描述外部接口,不提供具体的实现(从定义可知,协议一般只存在于头文件中)。协议定义可以参考下面例子。
@protocol Aprotocol <NSObject> //声明协议名称
@required//默认情况下不用写出
@property int x;
@property int y;
-(void) moveWithX:(int) x withY:(int)y;//声明方法
@optional
-(void) protocolNameB:(NSString*)string; //@optional可实现,也可以不用去实现。
@end
从上面的例子可以看出协议可以包含属性,实例方法成员,此外属性也可以包含类方法,初始化器和析构器等成员。
此外补充说明:协议里定义的属性与类中定义的属性有一点差别,就是协议中定义的属性编译器不会自动生成实例变量。
使用协议,一个类遵守协议,用<协议名>来表示,如上面的例子<NSObject>,也可参考下面的例子,这个NSObject并非是基类,而是一个协议的名称;一个类遵守协议,需要实现协议约定的所有@required成员。
@interface myClass <myProtocol>
@interface myClass :NSObject<myProtocol>
@interface myClass :NSObject<AProtocol, BProtocol>
注意第三个例子,表示类同时遵守AProtocol与BProtocol,类遵守这样的协议组合,需要实现协议中约定@required的所有的方法。
协议的继承,与类的单独继承不同,协议可以继承一个或多个协议。
看了协议的定义和使用,下面说明下为什么使用协议。官方文档对原因这样说明的:
To declare methods that others are expected to implement
To declare the interface to an object while concealing its class
To capture similarities among classes that are not hierarchically related
还有一个很重要的原因,那就是减少继承类的复杂性。这在将来IOS开发中很重要。
最后再看看其他常用的协议:
@类别与扩展
类别Category:支持在没有源代码的情况下,对于某些特定场合,为一个类增加功能。
类别可以添加类方法,实例方法或是重写积累方法,不能添加属性,实例变量和已有的同名方法。
类别适合在没有源代码的情况下,向已经封装的类中添加新的方法(如果你想扩充一个类的功能,但又不想使用继承,你可以选择类别。),或则是为类增加些特殊场景下需要实现的功能。
类别的命名规范,文件名:类名+扩展方法,如Fraction+Math1.h
类别的使用方法是@implementation Fraction (Math1),()就是说明Fraction使用类别。
扩展Extension:支持在类的源代码的前提下,向类添加功能。
扩展支持添加一下成员:添加属性,实例成员,类方法,实例方法,改写属性的读写属性。
扩展只能在.m实现文件内部访问,在类外不可以直接访问。
扩展主要用于信息隐藏,隐藏一些外部无需访问而内部实现有需要使用的方法、属性。