认识oc字符串NSString
NSString是一个Unicode编码、16位字符的字符序列
NSString被定义为类,引用类型,拷贝时具有引用语义
初始化方法:字面量初始化、初始化器、工厂方法
NSString拥有恒定性,所有的操作无法更改字符串本身,如有更改都是返回新值的形式
NSString拥有共享机制,引用计数管理对其有特殊的管理规则
<small>恒定性与共享机制相辅相成,正因为有了共享机制,才需要恒定性来保证原字符串不发生变化。同时共享机制也为系统内存管理带来了很大的好处,通常应用中字符串的用量很大,如果每个相同的字符串都有一个自己的内存空间,那就会造成很大的资源浪费。所以,在oc中,当我们申明一个字符串,而这个字符串在内存中已经存在,那么该指针就会指向已存在的字符串地址上</small>
NSMutableString
NSMutableString具有可变性,NSString具有恒定性
NSMutableString为NSString的子类
NSMutableString不具有共享性,NSString具有共享性
NSMutableString并不是在原有内存上直接增长,而是重新分配一个更大或更小的缓存容量存放字符
数组 列表 字典
<small>- 类型差不多,这里主要详细讲数组</small>
认识数组:
- 数组是一个有序的元素序列,支持随机存取。索引从0开始,索引访问越界会抛出运行时异常。注意与C语言不通。
- NSArray被定义为class,引用类型,拷贝时具有引用语义。
- NSArray的元素必须是对象,即NSObeject的子类:
1.如为基本数值类型,须用NSNumber封装为对象类型后,才能放入数组中。
2.如为C语言结构类型,须用NSValue封装为对象类型后,才能放入数组中。
3.数组元素可以是不用对象类型,可能会有类型不安全。
注:对于类型不安全问题,我们可以使用泛型来限制数组类型。ClassName<T>
4.NSArra具有常量性:长度和元素指针都不能更改。但指针指向的对象内部可以更改。
常用操作:
- 数组遍历:
1.最快——Fast Enumeration ,直接访问内存,优化索引检查,快5-10倍,用到了多线程。
2.较慢——NSEnumerator遍历:索引检查+动态消息调用//快速枚举 for ( BLNPoint* point in array5) { point.x++; point.y++; }
3.最慢——For循环访问:索引检查+动态消息调用//迭代器模式 NSEnumerator *enumerator = [array5 objectEnumerator]; BLNPoint* item; while (item = [enumerator nextObject]) { item.x++; item.y++; }
//for循环 for (int i=0; i<array5.count; i++) { NSLog(@"array5[%d],%@",i,array5[i]); }
- 注:for...in...利用了快速枚举NSFastEnumerate
当我们想要改变数组变量中的数据或者删除数组中的数据的时候,不能用for...in...。Objective-C中的foreach循环与Java中的相似,在内部是用iterator(迭代器)实现遍历的。而不管是在Java还是C++中,一旦修改了被遍历对象,在修改前生成的iterator都会失效,所以在用iterator遍历集合时增删集合元素
NSArray的枚举操作中有一条需要注意:对于可变数组进行枚举操作时,你不能通过添加或删除对象这类操作来改变数组容器。如果你这么做了,枚举器会很困惑,而你将得到未定义的结果。 - 解决方法:当我们需要改变数组中的内容时(这里指NSMutableArray可变数组),可以使用enumerateObjectsUsingBlock。
[arr enumerateObjectsUsingBlock:^(NSMutableDictionary *obj, NSUInteger idx, BOOL *stop) { if (...) { // do sth *stop = YES; // 相当于break ; stop控制跳出枚举器. } }];
//使用enumerateObjectsUsingBlock快速迭代,比起forloop要快。但是要再block中如果需要改变外部变量,那么需要在外部变量前声明__block
```
可变数组(NSMutableArray)
1.NSMutableArray支持更改数组长度和元素指针。为NSArray子类。
2.NSMutableArray初始化后,会分配一个缓存容量capacity,一般大于实际元素数量,当长度增长时,如实际需求大于capacity,其capacity会以近似二倍的方式指数增长。伴随代价:(1)分配新的堆内存2*capacity (2)将原来堆内存上的元素拷贝到新内存(3)释放原来的堆内存
3.最佳实践:估计好capacity,预先分配一定容量,避免以后增长
4.尽量避免使用insertObject:atIndex:和removeObjectAtIndex:等操作,因为会改变数组元素序列,涉及大量内存拷贝操作,代价高。
ARC
自动引用计数(Automatic Reference Counting)是OC默认的内存管理机制,针对堆上的对象,由编译器自动生成操作引用计数的指令(retain和release),来管理对象的创建与释放
新创建一个对象时,它的引用计数为1,retain操作计数+1,release-1;
当一个对象的引用计数变为0的时候,内存自动释放
自动释放池(Autorelease Pool)
release会导致对象立即释放。如果频繁对对象进行release,可能会造成琐碎的内存管理负担。autorelease可以将release的调用延迟到自动释放池被释放时。
推荐使用自动释放池(Autorelease Pool)Block,当其结束时,所有接受autorelease消息的对象将会被立即释放
大多数情况下,这样使用就行了,无需程序员干预
何时需要手工管理Autorelease Pool?
1.编写的程序不基于UI框架,如命令行程序@autoreleasepool { block块 todo
}
2.在循环中创建大量临时对象,需要更早地释放,避免临时对象聚集导致内存峰值过大
3.在主线程之外创建新的线程,在新线程开始执行处,需要创建自己的Autorelease Pool。
4.可以嵌套使用AutoreleasePool
认识协议 Protocol
协议:类型的合同约定,只描述外部申明,不提供具体实现。所以只有h文件
协议可以包含以下成员:
1.属性
2.实例方法
3.类方法
4.初始化器——不常用
5.析构器——不常用
协议中无法包含实例变量成员(实例变量是在m文件中)
协议中定义的属性本质上是访问器方法,编译器不会合成实例变量
使用协议
@interface ClassName : NSObject<ProtocolName>
在声明类文件时 ,在类名后面用<ProtocolName>
一个类遵守协议,需要实现协议约定的所有@required成员
<small>协议中的属性须在实现类的.h文件中申明(编译器合成实例变量需要,而方法可以省略)</small>
注意编译警告信息:
遵守协议后却没有实现必选协议方法时,会出现警告提示
协议类型变量被赋值非协议类型对象时,会出现警告提示协议本质上是一种类型,可以作为声明类型,但不能创建实例
检查协议类型
使用conformToProtocol:检查对象是否实现了协议
if([obj conformToProtocol:@protocol(Aprotocol)])
OC中的协议就是相当于Java中的接口(抽象类),只不过OC中的名字更形象点,因为我们在学习Java中的接口时候,看可以知道其实接口就相当于一种契约(协议),给他的实现类打上标记了,当然这个活在Java5.0之后,被注解替代了,因为注解就是为了此功能诞生的。
协议就是定义了一组方法,然后让其他类去实现
下面来看代码:
WithProtocol.h#import <Foundation/Foundation.h>
@protocol WithProtocol <NSObject>
//默认是必须实现的
//必须实现
@required
- (void)finshTask;
- (void)dontLate;
//可选实现
@optional
- (void)wearNeat;
@end
>
>这里就定义了一个协议WithProtocl
协议的定义格式:
@protocol 协议名 <父协议>
定义方法
@end
- 注:定义协议的关键字是@protocol,同时协议也是可以继承父协议的
>
协议中定义的方法还有两个修饰符:
@required:这个表示这个方法是其他类必须实现的,也是默认的值
@optional:这个表示这个方法对于其他类实现是可选的
这个就和类似与Java中的抽象类了,如果是abstract修饰的就必须实现,所以如果一个协议中没有@optional修饰的方法,那么这个协议就相当于Java中的接口了。
>
- 这里要注意的是,上面的代码中NSObject不是我们之前说的NSObject类了,而是NSObject协议,他也是OC中第一个协议,这个名字相同在OC中是没有关系的。
再看一下协议的使用:
>```
>**Student.h**
>#import <Foundation/Foundation.h>
>
>#import "WithProtocol.h"
>
>@interface Student : NSObject <WithProtocol>
>
>- (void)study;
>
>@end
>```
- 使用协议很简单,直接在继承类(NSObject)后面 <协议名>即可
>
>```
>#import "Student.h"
>@implementation Student
- (void)study{
NSLog(@"study");
}
//直接在.m文件中实现即可,不需要在.h文件中再次定义
#pragma mark - WithProtocol
//一般协议的方法,在这里标记下,用途是在导航栏快速地定位到这个方法是协议里的,起到分类的作用
- (void)finshTask{
NSLog(@"完成任务");
}
>- (void)dontLate{
//#warning 代码过几天在补充
NSLog(@"不迟到");
}
>- (void)wearNeat{
NSLog(@"穿戴整洁");
}
>@end
类别 Categroy
类别支持在没有源代码的情况下,基于某些特定场合,为一个类增加功能
可以添加:1.类方法;2.实例方法;3.重写基类方法
不能添加:1.属性;2.实例变量;3.已存在的同名方法
命名规范:类名+扩展方法,如:NSString+Drawing.h/.m使用类别:
1.使用场景:
- 适合在没有源代码的情况下,向已经封装的类中添加方法
- 为一个类在某些特殊场景下增加功能
- 对于复杂的大型文件分割实现
2.添加类别:
- 自己创建的类
- 系统的类
- 第三方库
BLNPoint.h #import <Foundation/Foundation.h> @interface BLNPoint : NSObject { float _weight; } @property NSInteger x; @property NSInteger y; -(void)move;
@end
BLNPoint+Drawing.h #import <Foundation/Foundation.h> #import "BLNPoint.h" @interface BLNPoint(Drawing) -(void)draw; //@property NSInteger weight; -(void)setWeight:(NSInteger)weight; -(NSInteger)weight; @end
扩展Extension
扩展支持在编译时、有类的源代码的情况下,向类添加功能。可以将扩展看做匿名的类别
接口定义在.m文件中@implementation前声明,实现代码仍然在@implementation中实现
扩展支持添加以下成员:
- 添加属性
- 添加实例成员
- 添加类方法
- 添加实例方法
- 改写属性的读写属性
@interface ClassName() { 声明定义 } @end @implementation ClassName { 具体实现 } @end
使用扩展:
- 扩展实现的成员都只能在.m实现文件内部访问,在类外不可以直接访问
- 扩展的主要用途在于信息隐藏,隐藏一些外部无需访问,而内部实现又需要使用的属性、方法:
1.类的主接口主要用于“对类外公开”的接口
2.类的扩展接口用于“对类内可见”的接口
<small>比如在类的主接口.h文件中有个属性声明为只读属性,我们可以在扩展中升级这个属性为读写属性,这样在类内实现可以使用读写属性,而对外只能是只读属性</small>