OC语言大集合

下面是我最近两年学习OC中的一些基础知识,对于学习OC基础知识的人可能有些帮助,拿出来分享一下,还是那句话不喜勿喷哦亲。。。。。

(1)

一、Objective-C概述

1. Object-C简称OC,扩充自C语言的面向对象编程语言

2. 苹果OS X和iOS操作系统的主要编程语言

3. OC的时间进度

1) 1980年布莱德·确斯发明OC语言

2) 1988年NextStep公司使用OC开发

3) 1996年OC成为苹果主要编程语言

4. OC语言的特点

1. C语言的超集,允许在OC中使用C语言源代码,编译器兼容C语言程序

2. C语言是面向过程,OC是面向对象编程

3. 包含一个运行时系统(Runtime)

4. 类库丰富

二、两种编程思想

1. 面向过程编程

1)分析解决问题的步骤,实现函数,依次调用

2)以过程为中心

2. 面向对象编程

1)分解问题组成的对象,协调对象间的联系和通信,解决问题

2)以对象为中心

PS: 对象定义了解决问题的步骤中的行为,不刻意完成一个步骤

3. 举例,设计五子棋软件

1) 面向过程编程设计

分析步骤

设计各类函数

一次调用完成功能

2) 面向对象编程设计

分析结构对象

根据功能定义类

使用对象功能完成程序

旗手对象:接受用户输入,通知棋盘对象在那个修改布局变化,利用规则对象对器具进行判定

规则对象:提供游戏规则

棋盘对象:绘制布局

3): 面向对象设计具有良好的可扩展性、可重用性

三、类和对象的基本概念

1. 类

具有相同特征和行为事物的抽象

导演类:

特征:姓名、性别、国籍

行为:拍电影、客串、X规则

练习:描述汽车类、手机类,自己再尝试列举类并描述

2. 对象

对象是类的实例,是一个具体的实例

冯小刚对象:

有姓名、有性别、有国籍

会拍电影、会客串、X规则

练习:iPhone4S是否是对象、波音747是否为对象、消失的MH370是否是对象,自己列举几个对象

3. 小结

1)类和对象是面向对象的核心

2)工作中就是不断的对应类,创建对象,使用对象

3)如何描述类

4)区分类和对象

四、OC中类的定义

定义类分两步:接口部分和实现部分

1. 接口部分:对外声明类的特征和行为

1) 用来声明类的特征和行为

2) 标志是@interface...@end

3) 类名、父类名、变量、方法,

4) 关键字@public

2. 实现部分:对内实现行为

1)用来实现方法,即实现类的行为

2)标志是@implementation...@end

3. 练习:

1) 定义奥迪汽车类

2) 定义手机类

五、类和文件

文件和类本质没有关系,为了方便管理,实现封装特性

1. 类:

@interface...@end

@implementation...@end

2. 文件

.h 称为接口文件或头文件 .m称为实现文件,用Xcode创建一个类时默认设置如下:

1)使用类名命名文件

2) .h 文件管理类的接口部分

.m 文件管理类的实现部分

3. 练习

1) 在.h文件中定义与文件不同名的类

2) 在.h文件中完成定义类

3) 在文件中定义多个类

六、使用类创建对象

1. 创建对象分两步

1)分配内存空间

根据类中声明的实例变量为对象分配内存

Person *p = [Person alloc];

+(id)alloc; +表示这个方法属于类,只能类执行,id表示任意类型的对象,即创建好的对象

2)初始化

对创建的对象进行实例变量的初始化设置

p = [p init];

-(id)init -表示这个方法属于对象,只能对象执行,id表示初始化完成的对象

3) 通常这两步需要连写

Person *p = [[Person alloc] init];



(2)

一、实例变量可见度

1. public(公有的)

实例变量可以被任意操作,实际情况中很少使用Public,有以下两个原因:

1)使用@public关键字,暴露了类内部的细节

2)不符合面向对象语言的三大特性之一 - 封装

2. protected(受保护的,默认的)

实例变量只能在该类和其子类中操作

3. private(私有的)

实例对象只能在该类内访问

4. 思考

如果在其他类中使用@protected修饰的实例变量

二、方法

1. 类方法

只能使用类来调用,在返回值类型的前面加+来标识

注:类方法中不能使用实例变量

2. 实例方法(对象方法)

只能使用对象调用

3. 方法声明

格式

- (返回类型)参数形容词1:(参数类型)参数名1 参数形容词2:(参数类型)参数名2

方法名呢?

参数形容词拼接起来就是该方法的方法名

4. 方法调用

[对象名或者类名 方法名];

比如:

[student sayHi];

正确表述:

给student对象发送sayHi消息

1)student接收到消息,及方法sayHi

2) student找到sayHi方法,并执行

5. 定义AodiCar类和MobilePhone类,需求:

1)实例变量使用默认可见度

2)添加方法实现对实例变量的操作

3)任意添加方法并使用

三、自定义初始化方法

1. -(id)init 这个初始化方法只能给实例变量设置默认值,不灵活,我们可以通过自定义初始化方法给实例变量设值

练习:

1. 给AodiCar类添加自定义初始化方法

2. 给MobilePhone类添加自定义初始化方法

四、OC中导入头文件的两种方式

1. #import

1. 导入头文件,即将导入的头文件中的内容,赋值到当前类

2. <>用于导入类库中的头文件

3. ""用于导入自定义的类

4. 功能类似C语言中的#include,但可以避免头文件被重复包含

2. @class

1. 告诉编译器有这个类,无法导入类的接口内容

2. 有效避免嵌套引用

五、复合

1. 多个组件组合在一起,形成一个整体,然后使用整体进行工作,这种组合成为复合。

2. OC中,类的实例变量由其他类型组成。

3. 简单的说就是让两个类关联起来就是复合

(3)

一、继承

1. 继承的上层:父类(或超类)

继承的下层:子类

2. 继承是单向的,两个类之间不能互相继承

3. 继承具有传递性

子类继承父类的特征和行为

4. 子类扩展父类,更具体

5. 继承特点:

1) OC中只允许单继承

2) 没有父类的类称为根类,OC中的根类是NSObject

3) 子类继承的内容

除了私有变量之外的所有实例变量和方法

4) 子类可以重写父类的方法

6. 练习

1)定义Person类

2)定义Student类,继承自Person类

3)使用继承得到的实例变量

4)使用继承得到的方法

5)定义CollegeStudent类,继承自Student类

7. 继承中方法的查找,查看课件中的图片

8. super

1)父类对象

2)可以执行父类中实现的方法

二、初始化方法

1. 初始化过程

1)执行父类中实现的初始化方法,向上递归到NSObject类中的初始化方法

2)判断父类中的初始化是否成功,即self是否存在

3)完成对象的初始化设置,返回对象

三、便利构造器

1. 作用

是一种快速创建对象的方式.它本质上是把初始化方法做了一次封装,方便外接使用.

2. 写法

类方法和对象方法的配合使用

(4)

一、 学习查看类的基本信息

1. 继承自谁

2. 遵循什么协议

3. 属于什么框架

4. 从iOS哪个版本开始支持

二、 字符串类

1. NSString:不可变字符串

对象创建赋值以后,内容和长度不能动态更改

2. NSMutableString:可变字符串

该类创建的字符串是一个动态的可变的字符串,可以支持增删改查等动态的操作

3. 联系

1) NSMutableString是NSString的子类,继承的到所有方法

2) 不可变字符串的修改操作,本质就是得到一个新的字符串

3) 不可变字符串的修改方法以string开头,有返回值

4) 可变字符串的修改方法没有返回值,修改原字符串

三、 字符串常用方法

1. 创建一个字符创常量

2. 快速创建一个字符串

3. 创建一个格式化的字符串

4. 字符串的比较

5. 字符串的转换

6. 字符串的截取与拼接

NSMutableString方法

7. 增

8. 删

9. 改

10.查

四、NSNumber,数值对象

用于将基本数据类型封装为对象

三、数组类

1. 特点

1)容器类,管理一组对象类型的数据

2)数组中元素是有序的,索引值从0开始

3) 数组中存储的元素必须是对象,类型任意

2. 分为可变数组与不可变数组

四、数组常用方法

1. 数组对象的创建

2. 获取数组中的元素个数以及访问数组元素

3. 追加数组中的内容

4. 数组转字符串

5. 判断数组中是否存在一个指定的对象

6. 根据指定的对象返回索引下标、返回数组中最后一个元素

不可变数组

7. 新增

8. 插入

9. 删除最后一个元素

10. 删除指定的元素

11. 删除指定位置的元素

12. 批量删除

13. 替换元素

(5)

查看API找方法

1. 判断指定字符串是否在字符串的头部

2. 判断指定字符串是否在字符串的尾部

3. 将数字型字符串转化为基本数据类型

一、数组的遍历

1. for循环

2. for..in循环

二、字典

特点:

利用键值对存取值

格式: @{@"key":@"value"}

相比于数组:

因为是通过key查找的,所以能比数组更快的查到所要找的数据

三、NSDictionary:不可变字典

字典内容确定后,不可再更改

1. 常用方法

字典对象的创建

获取字典的值

获取字典数量

获取字典所有的键和值

获取文件中的plist数据

枚举器访问

四、NSMutableDictionary:可变字典,项目开发中建议使用可变字典

1. 常用方法

创建指定元素个数的字典

增加元素

删除元素

全部删除

五、集合

特点:

OC中为我们提供了批量处理数据的一种数据类型-集合

与数组的区别:

1. 都是存储不同的对象的地址;不过NSArray是有序的集合,NSSet是无序的集合

2. 查找集合中的元素比数组速度更快,但是它没有顺序

3. 集合中不允许有重复的值

六、NSSet

常用方法

创建对象

枚举器访问集合元素 intersect

判断两个集合是否有交集

判断当前集合是否是子集

七、NSMutableSet

常用方法

创建指定元素个数的一个集合对象

添加一个对象到集合

把数组对象添加到集合中

从集合中删除一个对象

获得两个集合的并集

得到两个集合的交集

从一个集合中减去另一个集合

给集合赋值

八、数组排序

//默认升序

sortedArrayUsingSelector

//能指定升序还是降序

sortedArrayUsingComparator


(6)

一、拷贝对象的基本概念  1、拷贝一个对象作为副本,他会开辟一块新的内存来存储副本对象,就像复制文件一样,即源对象和副本对象是两块不同的内存区域  2、对象具备拷贝功能,必须实现协议

3、常用的可复制对象有NSNumber、NSString、NSArray、NSDictionary、NSMutableDictionary、NSMutableArray、NSMutableString

4、拷贝对象的种类:

1) copy: 产生对象的副本是不可变的

如果原来的对象是一个不可变的,那么他们的地址指向同一个地址,也就说他们是同一个对象

如果原来的对象是一个可变对象,那么他会新生成一个不可变对象,地址不同

2)MutableCopy: 产生的对象副本是可变的

得到一个新的可变对象

5、课堂实例

创建一个数组(包含元素:one,two,three等),然后分别对它进行保留、和拷贝操作,删除其中的一个元素,看看发生了什么?

二、浅拷贝和深拷贝基本概念和用法

1、浅拷贝只支付对象的本身,对象的属性、包含的对象不做拷贝

2、深拷贝则拷贝对象本身,对象的属性也会拷贝一份

3、Foundation框架中支持拷贝的类,默认是浅拷贝

4、课堂练习

一个数组包含了许多个对象,复制一个数组的副本,那么两个数组中的元素是没复制的,指向的还是同一个对象。

三、对象的自定义拷贝

1、对象拥有拷贝特性,必须实现NSCopying、NSMutableCopying协议,实现该协议的copyWithZone和MutableCopyWithZone方法

2、Objective-C对象的深、浅拷贝的区别就在于你对copyWithZone的不同实现

5、课堂练习

自定义类实现深、浅拷贝

6. 课堂实例

我们创建一个汽车类,汽车拥有一个Engine对象,一个车重和一个名字,让我们来一起实现下他的深复制和浅复制

四、总结

1、复制对象的概念,浅复制、深复制的区别。

2、自定义对象如何支持复制,实现NSCopying协议。

3、自定义对象如何实现深复制和浅复制。

4、Foundation框架中支持复制的对象需要注意的地方。

补充:

可以将NSZone,可以想象成一个内存池,alloc或是dealloc这些操作,都是在这个内存池中操作的。

cocoa总是会配置一个默认的NSZone,任何默认的内存操作都是在这个“zone”上操作的。


(7)

一、类目(category)的基本概念和用法  1、封装是面向对象的一个特征,OC也不意外,但有时候我们会碰到这样一种情况,比如我封装了一个类,不想再动它了,可是随着程序功能的增加,需要在哪个类中增加一个小小的方法,这是我们就不必再那个类中做修改或者在定义一个子类,只需要在用到那个方法时随手添加一个该类的类目即可    1) 在类目定义的方法,会成为原始类的一部分,与其他方法的调用没有区别    2) 通过给父类定义类目方法,其子类也会继承这些方法,如果子类添加类目方法,父类则不会拥有子类的类目方法  2、类目的应用和局限      1)应用        对现有类进行扩展:          比如,可以扩展Cocoa Touch框架中的类,你在类目中增加的方法会被子类所继承,而且在运行时跟其他的方法没有区别        作为子类的替代手段:          不需要定义和使用一个子类,可以通过类目直接向已有的类增加方法        对类中的方法归类:          利用category把庞大的类划分为小块分别进行开发,从而更好的对类中的方法进行更新和维护      2)局限性          无法向类目中,添加新的实例变量,类目中没有位置来容纳实例变量,如果想增加类的实例变量,只能通过定义子类的方式          在类目中一般不要覆盖现有类的方法二、延展基本概念和用法    类的延展就如同是"匿名"的类目,延展中声明的方法在类本身的@implementation和它对应的@end之间实现,类有时需要方法方法只有自己所见,我们可以通过延展的方式定义类的私有方法    //延展    @interface Person ()      - (void)privateMethod;    @end三、示例    创建一个MachinePerson类,让这个机器人能够说话,在不改变原类的基础上让这个机器人会跳舞    给NSArray添加一个类目,让NSArray能够将int型各位上的数放到一个数组中一、协议(protocol)的基本概念    1、协议的声明看起来比较类似一个类的接口文件,不同的是协议没有父类也不能定义实例变量。    2、协议是一种特殊的程序设计结构,用于声明专门被别的类实现的方法,协议在以下场合非常有用:      1)需要由别的类实现的方法      2)声明未知类的接口      3)两个雷之间的通信    3、协议的基本特点      1)协议可以被任何类实现方法,协议本身不是类,他是定义了一个其他类可实现的接口,类目也可以采用协议        2)协议的关键字          @required:表示必须强制实现的方法          @optional:表示可以有选择性的实现方法      3)实现的声明与实现          /* 协议的声明 */          @protocol HelloProtocol@optional          - (void)optionalMethod1;          - (void)optionalMethod2;          @required          - (void)requiredMethod1;          @end          /* 协议的实现 */          @implementation Person          - (void)requiredMethod1 {          } // 实现了该协议中的方法,且方法必须实现          - (void)optionalMethod1 {          } // 实现了该协议中的方法,可以选择不实现          @end                              /* 采用了该协议 */          @interface Person : NSObject@end

二、委托设计模式

1、指一个对象提供机会对另一个对象中的行为发生变化时做出的反应

如:当将一颗石子(对象1)丢入水中(行为发生变化,之前可能在你的手中)时,水面(对象2)泛起波纹(做出的反应)

2、基本思想:

两个对象协同解决问题,通常用于对象之间的通信

3、基本特点:

1)简化了对象的行为,最小化了对象之间的耦合度

2)使用代理,一般来说无需子类化

3)简化了应用程序开发,既容易实现,又灵活

三、 示例:中介找房

1、假设有一个Jack的人(Person),他想租一套公寓(Apartment),由于他工作繁忙(或者其他原因),没有时间(或者自身没有能力)去租房。因此,他委托中介公司(Agent)帮他寻找房源,找到合适的房源告知Jack。

2、补充

1)定时器

一旦创建了一个定时器对象(NSTimer实例),他可以按照一定时间的间隔,将指定消息发送到目标对象,并更新某个对象的行为,你可以选择在未来的某个时候将它"开启",或者将它停止乃至销毁。

2)NSRunloop

一个runloop就是一个事件处理的循环,用来不断的调度工作以及处理输入事件,使用runloop的目的是让你的线程在有工作的时候忙于工作,而没有工作的时候处于休眠状态

在我们的应用程序中,不需要创建NSRunloop对象,因为,在我们的主线程中(包含其他子线程)系统会自动穿件NSRunloop对象,如果需要访问当前线程中的runloop,你可以通过类方法『currentRunloop』获取到


(8)

一、引用计数器和对象所有权的基本概念

1、引用计数器

每个对象都会有一个引用计数器,当引用计数器为0是,系统就会将这个对象给释放

2、对象所有权

当一个所有者(owner,其本身可以是任何一个OC对象)做了以下某个动作时,它就拥有了对一个对象的所有权

1)alloc, allocWithZone:, copy, copyWithZone:, mutableCopy, mutableCopyWithZone:

2) 如果没有创建对象,而是将对象保留使用,同样拥有该对象的所有权

retain

3) 如果你拥有了某个对象的所有权,在不需要某一个对象时,需要释放他们

release autorelease

3. 案例

1) 假设在main函数主程序中,不小心想powerPC实例对象发送了release消息,即powerPC实例销毁了,但apple实例可能仍然在某个地方在使用powerPC实例,那么你的程序就会crash掉。

2) 我们知道2005年后,苹果的CPU转向了intel的怀抱,因此,我们需要将CPU改为intel的CPU

4. 详解delloc方法

什么时候调用

当对象的引用计数器为0时,系统会自动调用delloc方法,回收内存

为什么要调用父类的dealloc方法

子类的某些实例是继承自父类的,因此我们需要调用父类的delloc,释放父类拥有的这些对象

调用顺序

1)释放子类中的对象

2)释放父类中所拥有的实例

5. 案例

创建一个Vehicle类,以及Vehicle类的子类Car类,Vehicle类拥有一个实例变量_name,以及一个初始化名字的实例方法。

Car类自身拥有一个V6涡轮增加引擎。

6. 总结

该如何持有对象

1)初始化方法

2)直接向对象发送retain消息,持有dealloc方法释放该对象

3)

二、点语法的内存管理

1、赋值:

1)assign:直接赋值,默认

2)retain:赋值,并保留对象

3)copy:拷贝对象

2、读写性

1)readwrite:生成getter、setter方法,默认是readwrite

2) readonly:生成getter方法

3、原子性

1)atomic:多线程环境下, 存在线程保护,默认

2)nonatomic:多线程环境下,不存在线程保

三、ASSIGN、RETAIN与COPY的区别

1、assign

直接赋值,只是一个别名而已。

2、retain

保留的这个对象,两个对象指向了同一个位置。

3、copy

开辟了一个新的内存空间,分别指向了不同的内存位置,引用计数分别为1,与之前的对象完全脱离关系。这里我们尤其需要注意,某些时候copy的

作用相当于retain

四、自动释放池

1、基本概念

cocoa有一个自动释放池的概念,顾名思义,他是可以存放一些实体的集合,在这个自动释放池中的对象,是能够被自动释放的

NSObject类提供了一个autorelease消息,当我们向一个对象发送autorelease消息时,这个对象就被放入了自动释放池

2、 自动释放池的销毁时间

当我们想一个对象发送了autorelease消息是,当自动释放池销毁时,会对池中的每个对象发送一条release消息,以此释放他们

3、创建自动释放池

ios5.0 后写法

@autoreleasepool {}

ios5.0 之前写法

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];

[pool release];

4、示例

创建一个Person类,创建一个Person类的实例jack,将jack加入自动释放池,即向他发送一条autorelease消息,查看它的生命周期是怎样的。

完成第一点内容后,我们在向jack实例发送一条retain消息。查看它的引用计数和生命周期是如何的。

五、内存管理总结

1、当你使用new, alloc或copy方法创建一个对象时,该对象的引用计数器为1。当不再使用该对象时,你要负责向该对象发送一条release或者autorelease消息,这样,该对象将在其使用寿命结束时被销毁

2、你通过任何其他方法获得一个对象时,则假设该对象的引用计数为1,而且已经被设置为自动释放,你不需要执行任何方法来释放该对象。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。

3、如果你保留了某个对象,你需要释放或自动释放该对象,必须保持retain方法和release方法的使用次数相等。

4、除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain

5、大道至简

如果创建一个对象使用了alloc、copy[mutable]、retain,那么你就有义务向这个

对象发送一条release或者autorelease消息。

六、内存管理的两种方式

1. ARC 自动管理->程序员开辟内存,编译器帮助释放内存

iOS5.0的编译特性,它只允许用户开辟内存空间,不去释放空间,编译器帮程序员默认加了释放的代码

2. MRC 手动管理->程序员开辟内存,程序员释放内存

内存的开辟和释放都由程序代码进行控制,相对垃圾回收来说,对内存的控制更加灵活,可以在自己需要的释放的时候及时释放,对程序员的要求较高,程序员要熟悉内存管理机制。

内存管理机制:引用计数器

七、ARC和垃圾回收机制

1、垃圾回收机制

程序只需要开辟内存空间,不需要用代码显示的释放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再次分配,整个回收的过程不需要写任何代码,由系统自动完成垃圾回收

2、与java/net语言相同,oc2.0以后,也提供了垃圾回收机制,但在iOS移动终端设备中,并不支持垃圾回收机制(取决于终端设备的性能),因此iPhone并不能对内存进行自动垃圾回收处理(中间模式autorelease)

3、垃圾回收机制并不是ARC,ARC也是需要管理内存的,只不过是隐式的管理内存,编译器会在适当的地方自动插入retain.release和autorelease消息


(9)

一、NSFileHandle

1、对文件常做的处理

1)对文件内容进行读取和写入操作

2)做断点续传

2、处理文件的步骤

1)创建一个NSFileHandle对象

2)对打开的文件进行I/O操作

3)关闭文件

3、不足

只可以读写文件,不能创建文件,创建文件使用NSFileManager

二、常用方法

打开一个文件准备读取

+ (id)fileHandleForReadingAtPath:(NSString *)path;

打开一个文件准备写入

+ (id)fileHandleForWritingAtPath:(NSString *)path;

打开一个文件准备更新(读取、更新)

+ (id)fileHandleForUpdatingAtPath:(NSString *)path;

从设备或通道返回可用的数据

- (NSData *)availableData;

从当前的节点读取到文件末尾

- (NSData *)readDataToEndOfFile;

从当前节点开始读取指定的长度数据

- (NSData *)readDataOfLength:(NSUInteger)length;

写入数据

- (void)writeData:(NSData *)data;

获取当前文件的偏移量

- (unsigned long long)offsetInFile;

跳到指定文件的偏移量

- (void)seekToFileOffset:(unsigned long long)offset;

跳到文件末尾

- (unsigned long long)seekToEndOfFile;

关闭文件

- (void)closeFile;

三、练习

1、创建一个Date.text文件,获取当前的日期,将日期格式为“2013/02/14 05:20:00”的形式。然后一秒钟记录一次,将新的时间存入到文件中。

2、复制一个大型文件,为了节约内存,每次只读取500个字节。

(注意:最后一次读取可能超出500个字节,可以采取总长度减去已经读取的长度来判断,如果小于500个字节就不用读取500个字节直接读取到文件末尾就可以)

一、 文件管理常用的类和方法

1、NSFileManager:

提供了对文件的基本操作类,对文件的删除、修改、移动、复制等内容

2、常用方法

创建一个新的文件并写入数据

- (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr;

从一个文件中读取数据

- (NSData *)contentsAtPath:(NSString *)path;

srcPath路径上的文件移动到 dstPath 路径上

- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;

比较两个文件的内容是否一样

- (BOOL)contentsEqualAtPath:(NSString *)path1 andPath:(NSString *)path2;

文件是否存在

- (BOOL)fileExistsAtPath:(NSString *)path;

移除文件

- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;

二、属性列表化

iOS数据持久性的一种方式,使用方便简单快捷

只能将Bool、NSNumber、NSString、NSData、NSDate、NSArray、NSDictionary 写入属性列表plist文件

一、应用程序沙盒的基本概念

1、iOS中的沙盒(sandbox)机制

1)iOS应用程序只能对自己创建的文件系统读取文件,这个"独立","封闭","安全"的空间,称之为沙盒,所有的非代码文件都要保存在此。

2)它一般存放着你程序包文件(可执行文件)、图片、声音、视频、plist、sqlite数据库以及其他文件

2)每个应用程序都有自己的独立的存储空间(沙盒),一般情况下应用程序是不可以相互访问沙盒,也有例外,比如系统通讯录能在用户授权的情况下被第三方应用访问

2、沙盒目录文件的组成以及相关含义

当我们创建我们的应用程序时,在每个沙盒中默认含有四个文件,如下:

Documnets:

一般我们需要持久的数据都放在这个目录中,你可以在当中添加子文件夹,需要注意的是,iTunes备份和恢复的时候,会包括此目录

Library:

设置程序的默认设置和其他状态信息

Library/Caches:存放缓存文件,应用程序再次启动过程中需要的信息

Library/Preferences:包含应用程序的偏好设置文件。

tmp:

创建临时文件的目录,当我们的iOS设备重启是,文件会自动被清除

程序.app,与另三个路径的父路径不同

这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动

二、获取沙盒目录

1、获取到沙盒的根目录

NSString *homePath = NSHomeDirectory();

2、获取Documents目录

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,

NSUserDomainMask, YES);

NSString *docPath = [paths lastObject];

3、获取Library中的Cache

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES);

NSString *docPath = [paths lastObject];

4、获取tmp路径

NSString *temp = NSTemporaryDirectory();

5、获取程序包文件路径

[[NSBundle mainBundle] resourcePath

三、NSString类路径处理方法

有些时候我们需要对获取到的文件目录做一些特殊处理,常用的方法有以下几个:

比如针对:

NSString *path = @"/Users/apple/testfile.text";

//获得组成此路径的各个组成部分,结果:(“/”,” Users”, “apple”, ”testfile.text”)

- (NSArray *)pathComponents;

// 提取路径的最后一个组成部分,结果:testfile.text

- (NSString *)lastPathComponent;

// 删除路径的最后一个组成部分,结果:/Users/apple

- (NSString *)stringByDeletingLastPathComponent;

// 将path添加到现有路径的末尾,结果:/Users/apple/testfile.text/app.text

- (NSString *)stringByAppendingPathComponent:(NSString *)str;

// 取路径最后部分的扩展名,结果:text

- (NSString *)pathExtension;

// 删除路径最后部分的扩展名,结果:/Users/apple/testfile

- (NSString *)stringByDeletingPathExtension;

// 路径最后部分追加扩展名,结果:/Users/apple/testfile.text.jpg

- (NSString *)stringByAppendingPathExtension:(NSString *)str;

四、NSData的基本概念

NSData是用来包装数据用的

NSData存储的是二进制数据,这样就屏蔽了数据之间的差异,文本、音频、图像等数据都可以用NSData来存储

NSMutableData继承NSData类,可以对该对象进行数据修改

//string ---> data

NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];

//data ---> string

NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];


(10)

一、KVC(key value code)的基本概念和用法  1、基本概念        1)键-值编码是一个用于间接访问对象属性的机制,使用该机制不需要调用存取方法和变量实例就可以访问对象属性      2)通过KVC可以给readonly的属性赋值,      3)如果对象属性为基本数据类型时,我们存的时候需要将数据封装为NSNumber类型,系统内部存取时,系统会自动封装/解封      4)如果没有用@property声明,他讲在内部查找名为_key或key的实例变量      5)路径      除了通过键值设置值外,键/值编码还支持指定路径,通过.号隔开      6)一对多的关系      如果向NSArray请求一个键值,它实际上会查询数组中的每个对象来查找这个键值,然后将查询结果打包到另一个数组中并返回给你。      7)可以应用字符做简单运算,sum/min/max/avg/count      8)存取值格式        存值          setValue:forKey:          setValue:forKeyPath:        取值          valueForKey:          valueForKeyPath:  2、适用情况:将服务器的内容转化为数据模型,能够简化代码  3、练习      定义一个Person对象,他叫乔布斯,他有许多苹果设备:      1个macbook,价格8000      1个iPhone, 价格5000      1个iPad, 价格3800      使用kvc为Person设置名字, 使用kvc计算乔布斯所有设备的总价。    二、KVO(Key-Value-Observer)的基本概念和用法    1、基本概念      一种使对象获取其他独享的特定属性变化的通知机制    2、适用情况      主要用于试图,交互方面,比如界面的某些数据变化了,界面的显示也需要跟着变化,就需要建立数据和界面的关联    3、实现步骤      1)采用下面这个方法给属性添加观察者,在哪里注册观察者就要在哪里移除观察者          - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;      2)观察者实现下面方法,如果监听的属性发生变化,便会调用该方法。          - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary*)change context:(nullable void *)context;

3)个人习惯在delloc中释放

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;

4、练习:

监听狗的动作,做相应的回应

三、通知的基本概念和用法

1、基本概念

1)一种一对多的信息广播机制,一个应用程序同时只能有一个NSNotificationCenter对象

2)任何人都可以发送任何消息到消息中心,识别感兴趣通知的标示就是object+通知名称

2、适用情况

类与类之间传递信息

3、实现步骤

1)添加通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(paySalary) name:@"发工资啦" object:nil];

2)实现添加通知时方法选择器选择的方法

3)在需要发送通知的类中采用下面方法发送通知,发送成功便会执行步骤二实现的方法

[[NSNotificationCenter defaultCenter] postNotificationName:@"发工资啦" object:nil];

4)移除通知

[[NSNotificationCenter defaultCenter] postNotificationName:nil object:nil userInfo:nil];


(11)


一、归档的基本概念  之前将数据保存本地,只能是字符串、数组、字典、NSNumber、BOOL等容器类对象对象,不能将所有对象都给保存,而采用归档能将所有的对象转化为二进制数据保存在文件中,并通过解归档让将文件里面保存的数据读取出来二、使用环境    之前我们给通讯录添加一个联系人只能是将添加的人放到一个字典中,然后将这个字典放到数组中,最终将数组写入文件中    当我们需要显示这些联系人时,要从文件中将这个数组读取出来,还要将数据里面的一个个字典转化成model,放到一个新数组里    而现在我们可以使用归档在添加的时候就将这一个个联系人的信息转化成model,将这些model直接放到一个数组里,需要展示的时候,在从文件中读取出来数据,此时这个数组里面存放直接就是一个个model    有些应用支持一个离线缓存,也就是说当手机没联网时,可以将手机有网时的数据存放在本地,当手机没网时,从本地中取出来这些数据展示    三、某个对象支持归档解归档需要满足三个条件  1、所属的类遵守NSCoding协议  2、实现协议里面的归档方法      - (void)encodeWithCoder:(NSCoder *)aCoder  3、实现协议里面的解归档方法      - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder四、对系统的类进行归档解归档    1、指定将对象放在哪个文件中,归档后的文件,后缀要求是archiver      [NSHomeDirectory() stringByAppendingPathComponent:@"data.archiver"];    2、将对象归档到指定的路径中      [NSKeyedArchiver archiveRootObject:name toFile:path];    3、将归档后的数据提取出来      [NSKeyedUnarchiver unarchiveObjectWithFile:path];五、对自定义的类进行归档与解归档    1、让这个类遵循2、实现归档方法,aCoder就是归档时传过来的归档对象,对象被归档时会调用这个方法      - (void)encodeWithCoder:(NSCoder *)aCoder{            [aCoder encodeObject:self.name forKey:@"name"];        [aCoder encodeInteger:self.age forKey:@"age"];        [aCoder encodeObject:self.sex forKey:@"sex"];      }    3、实现解归档方法,对象解归档是会调用这个方法      - (instancetype)initWithCoder:(NSCoder *)aDecoder{          //解归档时会产生一个Person对象,这里是给这个Person对象赋值      self = [super init];            if (self) {                self.name = [aDecoder decodeObjectForKey:@"name"];        self.age = [aDecoder decodeIntegerForKey:@"age"];        self.sex = [aDecoder decodeObjectForKey:@"sex"];      }        return self;    }六、同时将多个对象归档与解归档    1、归档      1)准备一个可变的data对象,通过归档对象将多个数据存在一个data对象里,最终将这个data写入文件        NSMutableData *data = [NSMutableData data];      2)archiver初始化的时候包装一个可变的data对象        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];      3)通过归档对象将这些数据转化成二进制,并保存在一个data对象里        [archiver encodeObject:name forKey:@"name"];        [archiver encodeInteger:age forKey:@"age"];        [archiver encodeObject:sex forKey:@"sex"];      4)转化完毕,意思是结束使用归档对象将上面的数据保存在了data里面        [archiver finishEncoding];      5)将转化好的data写入文件        [data writeToFile:path atomically:YES];    2、解归档      1)将路径里的二进制数据给取出来                NSMutableData *data = [NSMutableData dataWithContentsOfFile:path];      2)将二进制数据包装在一个解归档对象中        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];      3)通过解归档对象将二进制数据分别给反序列化            NSString *name = [unarchiver decodeObjectForKey:@"name"];            NSInteger age = [unarchiver decodeIntegerForKey:@"age"];            NSString *sex = [unarchiver decodeObjectForKey:@"sex"];七、练习  1、模拟网络数据进行本地缓存    1)修改新工程自带的ViewController.h 如下      #import@interface ViewController : UITableViewController      @end    2)在AppDelegate.m里面自定义window,      self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];      [self.window makeKeyAndVisible];      //报错后,记得导入ViewController  #import "ViewController.h"      self.window.rootViewController = [[UINavigationController alloc]initWithRootViewController:[[ViewController alloc]init]];    3)新建一个InfoModel类,      InfoModel.h        #import//对象要归档必须要遵守NSCoding协议        @interface InfoModel : NSObject@property(nonatomic,copy) NSString *name;

@property(nonatomic,copy) NSString *phone;

@end

InfoModel.m

#import "InfoModel.h"

@implementation InfoModel

- (void)encodeWithCoder:(NSCoder *)aCoder{

[aCoder encodeObject:self.name forKey:@"name"];

[aCoder encodeObject:self.phone forKey:@"phone"];

}

- (instancetype)initWithCoder:(NSCoder *)aDecoder{

self = [super init];

if (self) {

self.name = [aDecoder decodeObjectForKey:@"name"];

self.phone = [aDecoder decodeObjectForKey:@"phone"];

}

return self;

}

@end

4) ViewController.m

#import "ViewController.h"

#import "InfoModel.h"

//报错,将课件中的MJExtension文件夹拖到工程中

#import "MJExtension.h"

//模拟本地缓存路径

#define kLocalPath [NSHomeDirectory() stringByAppendingPathComponent:@"data.archiver"]

//模拟服务器路径

#define kServerPath [[NSBundle mainBundle] pathForResource:@"Connect" ofType:@"plist"]

@interface ViewController ()

{

NSMutableArray *dataArray;

}

@end

@implementation ViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self loadData];

}

- (void)loadData{

//准备数据

dataArray = [self fetchData];

if(dataArray == nil){

NSLog(@"请检查网络设置");

return;

}

[self.tableView reloadData];

}

- (NSMutableArray *)fetchData{

//1、先从服务器获取数据

NSMutableArray *tempArray = [NSMutableArray arrayWithContentsOfFile:kServerPath];

if (tempArray == nil) {

//2、如果从服务器获取数据失败,则从本地缓存中读取数据

tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:kLocalPath];

}else{

//3、如果从服务器获取数据成功,则将数据通过MJExtension框架,转化为model

tempArray = [InfoModel mj_objectArrayWithKeyValuesArray:tempArray];

//4、将最新从服务器获取到数据保存到本地

[NSKeyedArchiver archiveRootObject:tempArray toFile:kLocalPath];

}

return tempArray;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

return dataArray.count;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

static NSString *cellID = @"cellID";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

if (cell == nil) {

cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellID];

}

InfoModel *model = dataArray[indexPath.row];

cell.textLabel.text = model.name;

cell.detailTextLabel.text = model.phone;

return cell;

}

@end


(12)

一、基本概念

全称是Grand Central Dispath,纯C语言,提供非常多强大的函数,是目前苹果官网推荐的多线程开发方式,NSOperation便是基于GCD的封装

二、GCD的优势所在

1、为多核的并行运算提出了解决方案

2、GCD会自动利用更多的CPU内核,比如双核、四核

3、GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

4、程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

三、GCD中有2个核心概念

1、队列:用来存放任务

1)串行队列

只有一个线程,加入到队列中的操作按添加顺序依次执行,一个任务执行完毕后,才能再执行下一个任务

2)并发队列

有多个线程,操作进来以后会将这些线程安排在可用的处理器上,同时保证先进来的任务优先处理

PS:GCD中还有一个特殊队列就是主队列,用来执行主线程的操作任务

2、任务:放在队列中执行

1)同步执行

只能在当前线程中执行任务,不具备开启新线程的能力

2)异步执行

可以在新的线程中执行任务,具备开启新线程的能力。

四、GCD做多线程开发可以抽象成两步

1、找到队列

1)找到更新UI的主线程所在的队列

dispatch_queue_t mainQueue= dispatch_get_main_queue();

2) 创建队列

dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL);

第一个参数:队列名字

第二个参数:队列类类型

并行队列:DISPATCH_QUEUE_CONCURRENT

串行队列:DISPATCH_QUEUE_SERIAL

3)系统内部给我们提供有一个现成的并发全局队列

dispatch_queue_t queue = dispatch_get_global_queue(0 , 0);

第一个参数:线程的优先级, DISPATCH_QUEUE_PRIORITY_BACKGROUND是最低的。

第二个参数:系统保留的参数,永远传0

2、在队列中确定想做的事

1) 使用同步的方式

dispatch_sync(queue, ^{

});

2)使用异步的方式

dispatch_async(queue, ^{

});

五、GCD创建的线程任务有四种执行方式

1、串行队列同步执行任务

dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(serialQueue, ^{

NSLog(@"-%@",[NSThread currentThread]);

});

dispatch_sync(serialQueue, ^{

NSLog(@"1 - %@", [NSThread currentThread]);

});

dispatch_sync(serialQueue, ^{

NSLog(@"2 - %@", [NSThread currentThread]);

});

dispatch_sync(serialQueue, ^{

NSLog(@"3 - %@", [NSThread currentThread]);

});

同步不具有开辟新线程的能力,不会开辟新的线程去执行任务,会在当前线程中顺序执行任务。

2、串行队列异步执行任务

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);

dispatch_async(serialQueue1, ^{

NSLog(@"1 = %@",[NSThread currentThread]);

});

dispatch_async(serialQueue1, ^{

NSLog(@"2 = %@",[NSThread currentThread]);

});

dispatch_async(serialQueue1, ^{

NSLog(@"3 = %@",[NSThread currentThread]);

});

异步具有创建新线程的能力,会开辟新的线程去执行任务,但由于是串行,里面只能创建一个线程,所以还是会按顺序执行

3、并行队列同步执行任务

dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(concurrentQueue, ^{

NSLog(@"1 = %@",[NSThread currentThread]);

});

dispatch_sync(concurrentQueue, ^{

NSLog(@"2 = %@",[NSThread currentThread]);

});

dispatch_sync(concurrentQueue, ^{

NSLog(@"3 = %@",[NSThread currentThread]);

});

同步不具有创建新线程的能力,不会开辟新的线程去执行任务,会在当前线程去执行任务

4、并发队列异步执行任务(常用)

dispatch_queue_t concurrentQueue1 = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQueue1, ^{

NSLog(@"1 = %@",[NSThread currentThread]);

});

dispatch_async(concurrentQueue1, ^{

NSLog(@"2 = %@",[NSThread currentThread]);

});

dispatch_async(concurrentQueue1, ^{

NSLog(@"3 = %@",[NSThread currentThread]);

});

并行队列可以里可以有多个线程,同步执行的方式又可以开辟多个线程,所以这里实现了多个线程并行执行,没有按照顺序

六、GCD组的应用

GCD中可以将一组相关联的操作,定义到一个群组中

定义到群组中之后,当所有线程完成时,可以获得通知

0、创建全局队列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

1、定义群组

dispatch_group_t group = dispatch_group_create();

2、定义群组的异步任务

dispatch_group_async(group, queue, ^{

});

dispatch_group_async(group, queue, ^{

});

3、群组任务完成通知

dispatch_group_notify(group, queue, ^{

});

1)dispatch_group_notify可以监听一组任务是否完成。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后,才通知界面说已经完成

2)如果不需要监听一组任务,可以直接使用dispatch_async方法

六、线程锁

1、在多线程应用中,所有被抢夺资源的属性需要设置为原子属性,atomic属性,必须与@synchronized(同步锁)一起使用

2、系统会在多线程抢夺时,保证该属性有且仅有一个线程能够访问

3、操作步骤

1)将资源属性设置原子属性

2)将处理该属性的代码放到线程锁中

@synchronized (self) {

}

-------------------------------------------------------------------

欢迎点赞收藏,喜欢的话就关注我,一起共勉,学习进步

推荐阅读更多精彩内容