1. 书籍《Objective-C编程全解》
[知识点]
- 面向对象编程的概念
- 类和继承
- 对象的类型和动态绑定
- 内存管理
- Runtime
- Foundation框架中的类
- Category
- 类簇
- 协议
- 对象的复制和存储
- Block
- 消息发送模式
- 异常和错误
- 并行编程
- KVC,KVO
2. 电影列表页 demo
[知识点]
- AutoLayout
- Masonry
- Mantle
- UITabBarController
- UINavigationBar
- UITableView
- UIScrollView
- NSURLConnection
- NSURLSession
3.工具的使用
- git
- vim
- CocoaPods
- Reveal
面向对象编程的概念
面向对象:通过消息协调各个对象之间的消息发送,使其作为一个整体运行,这就是面向对象的软件的运行模式。(ps:对象都有属性,并且能够接收消息。)
用类创建对象的过程叫做实例化,生成的对象叫实例对象(实例)。
类和继承
通过扩展或者修改既有类来定义新类的方法叫作继承。
继承的好处:代码重用;继承的缺点:父类的改变影响所有的子类。子类与父类耦合度很高。当子类中需要有自己独特的行为,不想使用父类的方法,可以把父类的方法覆盖掉:直接在子类中用一样的名字写个方法。在继承体系中方法调用的顺序:1)在自己类中找;2)如果没有就去父类中找;3)如果父类中没有,就去父类的父类中找……直到找到基类。
OC中,类方法也是可以继承的(通过子类的类名调用父类的类方法);类方法也是可以重写的;类方法可以和对象方法重名(+表示类方法,-表示对象方法);子类中不能定义与父类中同名的成员变量。OC是单继承(一个类只能继承一个父类),并且可以多层继承。
对象的类型和动态绑定
动态绑定就是指在程序执行时才确定对象的属性和需要相应的消息。
OC中的消息实在运行时才去绑定的。运行时系统首先会确定接收者的类型(动态类型识别),然后根据消息名在类的方法列表里选择相应的方法执行,如果没找到就到父类中继续寻找,如果一直找到NSObject也没找到要调用的方法,就会报告不能识别的错误。
OC的静态类型检查是在编译期完成的。
1)id类型的变量,调用任何方法都能够通过编译。
2)id类型的变量和被定义为特定类的变量之间是可以相互赋值的。
3)被定义为特定类对象的变量,调用类或父类未定义的方法会提示警告。
4)若是静态类型的变量,子类类型中实例变量可以赋值给父类类型的实例变量。
5)若是静态类型的变量,父类类型的实例变量不可以赋值给子类类型的实例变量。
6)若是要判断到底是哪个类的方法被执行了,不要看变量所声明的类型,而要看实际执行时这个变量的类型。
7)id类型不是NSOject* 类型(id类型和其他类型并没有继承关系)。
内存管理
1)引用计数:是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。
当创建一个对象的实例并在堆上申请内存时,对象的引用计数就为1,在其他对象中需要持有这个对象时,就需要把该对象的引用计数加1,需要释放一个对象时,就将该对象的引用计数减1,直至对象的引用计数为0,对象的内存会被立刻释放。
- 自己生成的对象,自己持有。
- 非自己生成的对象,自己也能持有。
- 不在需要自己持有对象的时候,释放。
- 非自己持有的对象无需释放。
2)ARC:ARC通过在编译期间添加合适的retain/release/autorelease等函数,来确保对象被正确释放。编译器会根据传入的变量是局部变量还是引用变量,返回对象的方法是不是初始化方法等信息来推断应当在何处加入retain/release/autorelease等函数。ARC只能管理OC的对象,不能管理通过malloc申请的内存(malloc是c语言中的动态内存分配函数)。使用ARC时应该尽量保证对象之间的关系呈树形结构。
__strong 是默认使用的标识符。只有还有一个强指针指向某个对象,这个对象就会一直存活。
__weak 声明这个引用不会保持被引用对象的存活,如果对象没有强引用了,弱引用会被置为 nil
__unsafe_unretained 声明这个引用不会保持被引用对象的存活,如果对象没有强引用了,它不会被置为 nil。如果它引用的对象被回收掉了,该指针就变成了野指针。
__autoreleasing 用于标示使用引用传值的参数(id *),在函数返回时会被自动释放掉。
3)垃圾回收:指的是在程序运行过程中,检查是否有不再使用的对象,并自动释放它们所占用的内存,通常被简称为GC,内存的检查和回收都是由垃圾收集器完成的(garbage collector)。( iOS 平台不支持)。
Runtime
Objective-C是动态语言,它将很多静态语言在编译和链接时做的事放到了运行时,这个运行时系统就是 Runtime。Runtime 是一个库,这个库使我们可以在程序运行时创建对象、检查对象,修改类和对象的方法。
静态语言:在编译的时候会决定调用哪个函数。
动态语言(OC):在运行的时候根据函数的名称找到对应的函数来调用。
isa:OC 中,类和类的实例在本质上没有区别,都是对象,任何对象都有isa 指针,它指向类或元类。
SEL:SEL(又叫选择器)是方法的 selector 的指针。方法的 selector 表示运行时方法的名字。OC在编译时,会依据每一个方法的名字、参数,生成一个唯一的整型标识(Int类型的地址),这个标识就是 SEL。
IMP:IMP 是一个函数指针,指向方法最终实现的首地址。SEL 就是为了查找方法的最终实现IMP。
Method:用于表示类定义中的方法,它的结构体中包含一个 SEL 和 IMP,相当于在 SEL 和 IMP 之间作了一个映射。
消息机制:任何方法的调用本质就是发送一个消息。编译器会将消息表达式[receiver message]转化为一个消息函数 objc_msgSend(receiver, selector)。
Runtime 的使用:获取属性列表,获取成员变量列表,获得方法列表,获取协议列表,方法交换,动态的添加方法,调用私有方法,为分类添加属性。
Foundation框架中的类
Foundation框架包括:根对象类(NSObject)、表示基本数据类型的类(如字符串和字节数组)、存储其他对象的集合类、表述系统信息和集合的类。
根对象类(NSObject及NSCopying协议一起)定义了基本的对象属性和行为。
Foundation框架提供了很多基本类型,包括数字(NSNumber)和字符串(NSString)。还提供了一些表述其他对象的类,如数组(NSArray)和字典集合(NSDictionary)类。
Foundation框架提供了访问核心操作的类,如锁、线程和计时器。
- 字符串类:
- NSString和NSMutableString:不可变字符串和可变字符串
- Foundation框架之字符串类总结
- 数组类:
- NSArray和NSMutableArray:不可变字节数组和可变字节数组
- Foundation框架之数组类总结
- 字典类:
- NSDicitonary和NSMutableDictnary:不可变字典和可变字典
- Foundation框架之字典类总结
- 数字类:
- NSNumber:数字对象
- Foundation框架之数字、结构体、日期、文件类总结
- 结构体类:
- CGPoint:定义矩形原点坐标
- CGSize:定义矩形尺寸的结构体
- CGRect:同时定义矩形原点和尺寸的结构体
- NSRange:描述位置和大小范围的结构体
- NSValue:将结构体转换为对象的类
- Foundation框架之数字、结构体、日期、文件类总结
- 日期类:
- NSDate和NSCalendar:表示时间和日期的类
- Foundation框架之数字、结构体、日期、文件类总结
- 文件类:
- NSFileManager:管理文件系统的类
- Foundation框架之数字、结构体、日期、文件类总结
Category
实现某个类的一部分方法的模块叫做范畴或类别(category)
category和类一样,在接口文件中声明,在类文件中实现。但是范畴中不能声明实例变量,只能声明方法,声明的方法既可以是类方法也可以实例方法。使用场景:给现有的类添加方法;将一个类的实现拆分成多个独立的源文件;声明私有的方法。
@interface 类名 (范畴名)
方法的声明;
@end
//类名部分为范畴所属的类的名字或即将添加该范畴的类的名字。类名必须是已经存在的类,不能为一个不存在的类定义范畴
@implementation 类名 (范畴名)
方法的定义;
@end
//实现部分除了不可以定义新的实例变量外,都和传统方式的实现文件一样
extension是在编译期决定,category由运行期决定,这就是他们不同的根本之处。它决定了他们之间的分工与区别。extension的生命周期跟随主类,用于隐藏私有信息,你必须拥有这个类的实现/源码,你才可以为它添加extension。category无法添加实例变量,在运行期间,对象内存布局已经确认,这时你无法破坏已经存在的内存空间,所以无法进行实例变量的添加。
类簇
类簇:定义相同接口并提供相同功能的一组类的集合。仅公开接口的抽象类也称为类簇的公共类。类簇是Foundation框架广泛使用的设计模式。类簇在公共抽象超类下对多个私有的具体子类进行分组。以这种方式对类进行分组简化了面向对象框架的公共可见体系结构,而不会降低其功能丰富度。类簇是基于抽象工厂设计模式的。
协议
对象的主要作用是表示所处理的消息的类型,而表示对象的作用和行为的集合体就称为协议。作用如下:
- 定义一套公用的接口(Public)
@required:必须实现的方法,默认在@protocol里的方法都要求实现。
@optional:可选实现的方法(可以全部都不实现) - 委托代理(Delegate)传值:
它本身是一个设计模式,它的意思是委托别人去做某事。
比如:两个类之间的传值,类A调用类B的方法,类B在执行过程中遇到问题通知类A,这时候我们需要用到代理(Delegate)。
又比如:控制器(Controller)与控制器(Controller)之间的传值,从C1跳转到C2,再从C2返回到C1时需要通知C1更新UI或者是做其它的事情,这时候我们就用到了代理(Delegate)传值。 - 声明若干个方法(不能声明成员变量)
- 只要某个类遵守了这个协议,就拥有了该协议中的所有方法声明,类对象 可直接调用方法
- 只要父类遵守了某个协议,其子类也跟着遵守
- 一个类可以遵循多个协议
- 协议可以遵守协议。一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明。
对象的复制和存储
浅拷贝:只复制对象的指针(共享变更操作的结果)
深拷贝:复制具有新的内存空间的对象(独自管理)
不可变对象:进行copy得到的是浅复制,进行mutableCopy得到的是深复制。
可变对象:无论进行copy还是mutableCopy都是深复制。
本地存储的四种方法:
- NSKeyedArchiver归档(NSCoding)序列化:将对象打包成二进制文件(类定义或对象间的关系改变的时候,归档的方法也必须改变,XML或属性表等高通用性的格式来保存数据,从程序的效率及稳定性上会更好)归档的逆变换称为解档
- NSUserDefaults:用来保存应用程序设置和属性、用户保存的数据。
- NSFileManager write 的方式直接写入磁盘:plist文件保存,plist本身就是XML文件,名字后缀为.plist。plist主要保存的数据类型为NSString、NSNumber、NSData、NSArray、NSDictionary。
- SQLite:采用SQLite数据库来存储数据
Block
Block本质上也是一个OC对象,它内部也有个isa指针
Block是封装了函数调用以及函数调用环境的OC对象
Block的代码是内联的,效率高于函数调用
Block对于外部变量默认是只读属性
Block被Objective-C看成是对象处理
一个 Block 实例实际上由 6 部分构成:
isa 指针,所有对象都有该指针,用于实现对象相关的功能。
flags,用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用。
reserved,保留变量。
invoke,函数指针,指向具体的 block 实现的函数调用地址。
descriptor, 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。
variables,capture 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。
在 Objective-C 语言中,一共有 3 种类型的 block:
_NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
_NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
_NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁
消息发送模式
run loop/ event loop(运行回路/事件循环):应用从操作系统中接受鼠标点击等事件的消息,并将其转到相应的例行程序来处理。
应用和运行回路的处理流程:运行回路从操作系统(窗口服务器)中接受事件,并根据事件种类和状态来调用相应的例行程序,同时忽视那些没必要处理的事件。事件到来随机,当应用程序在处理某个消息的时候,新接收到的消息就不能被立即被处理,而是被放入等待队列中,如果没有消息到来,应用会进入休眠等待的状态。
delegate:当对象需要根据用途改变或增加新功能时,为了执行新添加的处理,就需要引用一个特殊的类似于被咨询者的对象,delegate可以在运行时动态分配。(某对象接收到不能处理的消息时让其他对象代为处理的一种方式)
通知中心:期望取得通知的对象预先向通知中心注册期望取得的通知。某对象向通知中心(notification center)发送请求,这称为发送(post)通知,只要注册过该通知的对象,都会获得通知中心推送的消息。
反应链(responder chain)是具有层次结构的GUI组件间自动发送消息的一种方式。处理消息的候补组件对象如算珠般串联在一起,将消息传递给这之间的某个对象时,在发现可以处理该消息的对象之前,会顺次向后面的对象“转交”该消息,这样的构成就称为反应链。组件都是UIResponder类的子类。窗体之外是NSView的子类,NSView同时也是NSResponder的子类。各窗体中,最初被发送消息的对象称为第一反应者(first responder)。
异常和错误
异常exception是指必须中断程序的正常执行来处理的特殊状态。
异常处理机制 exception handling mechanism:当异常发生时的处理和程序原本应该执行的处理分开进行的结构。当异常状况产生时,程序会自动脱离出普通的函数和方法调用,转而执行异常处理的过程。
@try:将可能出现异常的代码放在@try块中定义
@catch:所有的一场逻辑都放在@catch块中进行处理
@finally:最后应用@finally块来进行资源回收,@finally块中的内容是肯定会被执行的。因此,一般不要在@finally中使用return、@throw等导致方法终止的语句,一旦@finally块中使用了return、@throw语句,就会导致@try以及@catch块中的return、@throw语句失效。
@try
{
//业务实现代码
}
@catch (异常1 ex)
{
//异常处理代码
}
@catch (异常2 ex2)
{
//异常处理代码
}
//可能更多的@catch块
@finally
{
//资源回收
}
如果执行@try块里的业务逻辑代码出现异常,系统将自动生成一个一场对象,该异常对象被提交给系统,这个过程被称之为抛出(throw)异常。当运行环境接收到异常对象时,会依次判断该异常对象事都是@catch块后异常类或其子类的实例,如果是,那么运行话你就能够调用该@catch块来处理该异常这个过程称之为捕获(catch)异常,如果不是则再次用该异常对象和下一个@catch块里的异常类进行比较,如果系统无法找到处理该异常的@catch块,程序就此退出。@finally块是在@try块或@catch块执行完毕准备退出时执行,而且是必须执行的。
并行编程
串行(Serial):在固定时间内只能执行单个任务。例如主线程,只负责 UI 显示。
并发(Concurrent):在固定时间内可以执行多个任务。它和并行(Parallel)的区别在于,并发不会同时执行多个任务,而是通过在任务间不断切换去完成所有工作。
同步(Sync):会把当前的任务加入到队列中,除非该任务执行完成,线程才会返回继续运行,也就是说同步会阻塞线程。任务在执行和结束一定遵循先后顺序,即先执行的任务一定先结束。
异步(Async):会把当前的任务加入到队列中,但它会立刻返回,无需等任务执行完成,也就是说异步不会阻塞线程。任务在执行和结束不遵循先后顺序。可能先执行的任务先结束,也可能后执行的任务先结束。
并行操作的三种方式:
NSThread
可以最大限度地掌控每一个线程的生命周期。但也需要开发者手动管理所有的线程活动。总体使用场景很小,基本是在开发底层的开源软件或是测试时使用。
GCD
官推。它将线程管理推给系统,用名为Dispatch Queue的队列;使用时只需定义每个线程需要执行的任务。所有的工作都是先进先出,每个block运作速度极快(纳秒级别)。一般为了追求高效处理大量并行数据,如异步加载图片、网络请求等。
Operations
与GCD类似。是Operation Queue队列实现,但并不局限于先进先出的队列操作。它提供了多个接口可以实现暂停、继续、终止、优先顺序、依赖等复杂操作,比GCD更加灵活。Operations应用场景较广,处理速度较快(毫秒级别)。几乎所有的基本线程操作都可以实现。
并行编程的问题:
竞态条件
多个线程对共享的数据镜像读写,导致最终的数据结果不确定。
优先倒置
低优先级的任务会因为某种原因先于高优先级的任务执行。
死锁问题
多个线程直接互相等待彼此停止执行,以获取某种资源,但是没有一方会提前退出的情况。
GCD的方法:
dispatch_async
对某个线程进行异步操作。
dispatch_after
一般用于主线程的延时操作。
dispatch_once
用于确保单例的线程安全。
dispatch_group
一般用于多个任务同步。当多个任务关联到同一个群组(group)后,所以的任务执行完后,在执行一个统一的后续工作。这里需要注意dispatch_group_wait是一个同步操作,它会阻塞线程。dispatch_group_notify:当任务管理组中的任务都已经执行完了会通知这个函数执行。
全局队列中的优先级:
background
用来处理特别耗时的后台操作,例如同步、备份。
utility
用来处理需要一些时间而又不需要立即返回结果的操作,特别适用于异步操作,例如下载、导入数据。
default
user-Initiated
用来处理用户触发的需要立即返回结果的操作,例如打开点击的文件。
user-Interactive
用来处理与用户交互的操作。一般用于主线程。如不及时响应,则有可能阻塞主线程的操作。
unspecified
未确定优先级,有系统根据不同环境推断
KVC,KVO
KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。
KVC的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用KVC,下面是KVC最为重要的四个方法:
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
KVC设值:
KVC要设值,那么就要对象中对应的key,KVC在内部是按什么样的顺序来寻找key的。当调用setValue:属性值 forKey:@”name“的代码时,底层的执行机制如下:
如果没有找到Set
如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set:属性名时,会直接用setValue:forUndefinedKey:方法。
KVC取值:
当调用valueForKey:@”name“的代码时,KVC对key的搜索方式不同于setValue:属性值 forKey:@”name“,其搜索方式如下:
- 首先按get,,is的顺序方法查找getter方法,找到的话会直接调用。如果是BOOL或者Int等值类型, 会将其包装成一个NSNumber对象。
- 如果上面的getter没有找到,KVC则会查找countOf,objectInAtIndex或AtIndexes格式的方法。如果countOf方法和另外两个方法中的一个被找到,那么就会返回一个可以响应NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子类),调用这个代理集合的方法,或者说给这个代理集合发送属于NSArray的方法,就会以countOf,objectInAtIndex或AtIndexes这几个方法组合的形式调用。还有一个可选的get:range:方法。所以你想重新定义KVC的一些功能,你可以添加这些方法,需要注意的是你的方法名要符合KVC的标准命名方法,包括方法签名。
- 如果上面的方法没有找到,那么会同时查找countOf,enumeratorOf,memberOf格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet所的方法的代理集合,和上面一样,给这个代理集合发NSSet的消息,就会以countOf,enumeratorOf,memberOf组合的形式调用。
- 如果还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),那么和先前的设值一样,会按_,_is,,is的顺序搜索成员变量名,这里不推荐这么做,因为这样直接访问实例变量破坏了封装性,使代码更脆弱。如果重写了类方法+ (BOOL)accessInstanceVariablesDirectly返回NO的话,那么会直接调用valueForUndefinedKey:方法,默认是抛出异常。
KVO 即 Key-Value Observing,翻译成键值观察。它是一种观察者模式的衍生。其基本思想是,对目标对象的某属性添加观察,当该属性发生变化时,通过触发观察者对象实现的KVO接口方法,来自动的通知观察者。
观察者模式:一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的。观察者模式较完美地将目标对象与观察者对象解耦。
简单来说KVO可以通过监听key,来获得value的变化,用来在对象之间监听状态变化。KVO的定义都是对NSObject的扩展来实现的,Objective-C中有个显式的NSKeyValueObserving类别名,所以对于所有继承了NSObject的类型,都能使用KVO。
如果我们已经有了包含可供键值观察属性的类,那么就可以通过在该类的对象(被观察对象)上调用名为 NSKeyValueObserverRegistration 的 category 方法将观察者对象与被观察者对象注册与解除注册:
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
observer:观察者,也就是KVO通知的订阅者。订阅着必须实现
observeValueForKeyPath:ofObject:change:context:方法
keyPath:描述将要观察的属性,相对于被观察者。
options:KVO的一些属性配置;有四个选项。
context: 上下文,这个会传递到订阅着的函数中,用来区分消息,所以应当是不同的。
options所包括的内容
NSKeyValueObservingOptionNew:change字典包括改变后的值
NSKeyValueObservingOptionOld:change字典包括改变前的值
NSKeyValueObservingOptionInitial:注册后立刻触发KVO通知
NSKeyValueObservingOptionPrior:值改变前是否也要通知(这个key决定了是否在改变前改变后通知两次)
AutoLayout
AutoLayout则是苹果公司在iOS6推出的一种基于约束的,描述性的布局系统。1).基于约束:和以往定义frame的位置和尺寸不同,AutoLayout的位置确定是以所谓相对位置的约束来定义的,比如x坐标为superView的中心,y坐标为屏幕底部上方10像素等
2).描述性: 约束的定义和各个view的关系使用接近自然语言或者可视化语言的方法来进行描述
3).布局系统:即字面意思,用来负责界面的各个元素的位置。
NSLayoutConstraint 约束满足公式:
item1.attribute = multiplier ⨉ item2.attribute + constant
添加对于两个同层级view之间的约束关系,添加到他们的父view上
对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上
Masonry
Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了,并具有高可读性。
Masonry的属性:
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;
例:
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
//也可以写成
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
.equalTo相当于NSLayoutRelationEqual
.lessThanOrEqualTo相当于NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo相当于NSLayoutRelationGreaterThanOrEqual
//例
// width> = 200 && width <= 400
make.width.greaterThanOrEqualTo(@ 200);
make.width.lessThanOrEqualTo(@ 400);
优先级:
.priority 允许您指定确切的优先级
.priorityHigh相当于UILayoutPriorityDefaultHigh
.priorityMedium是高低之间的一半
.priorityLow相当于UILayoutPriorityDefaultLow
//例
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
Mantle
Mantle的目的是简化Cocoa和Cocoa Touch应用的model层
最主要的就是二个类和一个协议,即:
- MTLModel类:通常是作为我们的Model的基类,该类提供了一些默认的行为来处理对象的初始化和归档操作,同时可以获取到对象所有属性的键值集合。
- MTLJSONAdapter类:用于在MTLModel对象和JSON字典之间进行相互转换,相当于是一个适配器。
- MTLJSONSerializing协议:需要与JSON字典进行相互转换的MTLModel的子类都需要实现该协议,以方便MTLJSONApadter对象进行转换。
以GHIssue为例,通常会以以下方式来定义Model:
//Model继承了通常是MTLModel类,同时实现了MTLJSONSerializing协议
@interface GHIssue : MTLModel
@property (nonatomic, copy, readonly) NSURL *URL;
@property (nonatomic, copy, readonly) NSURL *HTMLURL;
@property (nonatomic, copy, readonly) NSNumber *number;
@property (nonatomic, assign, readonly) GHIssueState state;
...
@end
//实现MTLJSONSerializing协议的+JSONKeyPathsByPropertyKey类方法
//将属性名的键值与JSON字典的键值做一个映射
//MTLJSONAdapter对象便可以自动进行赋值操作和编码解码操作
@implementation GHIssue
...
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"URL": @"url",
@"HTMLURL": @"html_url",
@"reporterLogin": @"user.login",
@"assignee": @"assignee",
@"updatedAt": @"updated_at"
};
}
...
@end
Model对象的属性与JSON数据之间的映射是通过字典来实现的。通过这种对应关系,Model对象便可以和JSON数据相互转换。需要注意的是返回中字典中的key值在Model对象中必须有对应的属性,否则Model对象将无法初始化成功。
//Model对象的属性与JSON数据之间的转换
@implementation GHIssue
...
+ (NSValueTransformer *)URLJSONTransformer {
return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}
+ (NSValueTransformer *)HTMLURLJSONTransformer {
return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}
+ (NSValueTransformer *)stateJSONTransformer {
return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{
@"open": @(GHIssueStateOpen),
@"closed": @(GHIssueStateClosed)
}];
}
...
@end
//通过MTLJSONAdapter类来适配MTLModel对象和JSON数据
//根据JSON字典创建一个GHIssue对象完成
NSError *error = nil;
NSDictionary *JSONDictionary = ...;
GHIssue *issue = [MTLJSONAdapter modelOfClass:GHIssue.class fromJSONDictionary:JSONDictionary error:&error];
//从这个对象中获取到相应的JSON字典
NSDictionary *JSONDictionary = [MTLJSONAdapter JSONDictionaryFromModel:issue];
UITabBarController
- 通过赋予UITabBarController的属性viewControllers,来配置每个tab切换所对应的controller。viewControllers中每个元素的顺序决定了在页面中哪个tab对应的view会默认显示,如果要手动指定一个要显示的view,通过指定属性selectedViewController来设置默认指定的view。或者使用selectedIndex也可以来指定要默认显示的view。
- 每个tabbar的tabbar item是根据它自身对应的controller来进行配置的。如果要让tabbar item和其相关的controller进行关联,那么就要创建一个UITabBarItem实例,然后指定给响应的controller。例如:
firstViewController.tabBarItem = [[UITabBarItem alloc] init]; 若没有指定特定的tabBarItem,那么当前页面对应的viewcontroller会创建一个默认的没有图片的item且item的title为viewcontroller的title内容。 - UITabBarControllerDelegate protocol.任何一个tab切换都会触发与delegate发送消息。通过delegate可以完成特定tabbar 被选中的时候执行额外的方法。这些delegate方法都是可选的。
UITabBarController 本身并不会显示任何视图,如果要显示视图则必须设置其 viewControllers 属性(它默认显示 viewControllers[0])。这个属性是一个数组,它维护了所有 UITabBarController 的子控制器。
为了尽可能减少视图之间的耦合,所有的 UITabBarController 的子控制器的相关标题、图标等信息均由子控制器自己控制,UITabBarController 仅仅作为一个容器存在。
UITabBarController 生命周期:
1.把子控制器都添加给 TabBarController 管理,当程序启动时它只会加载第一个添加的控制器的view。
2.切换到第二个界面。先把第一个界面的view移开,再把新的view添加上去,但是第一个view只是被移开没有被销毁。
3.重新切换到第一个界面,第一个的控制器直接 viewWillAppear,不会再执行 viewDidLoad 方法。第二个界面中的 view 移除后并没有被销毁(因为它的控制器还存在,有一个强引用引用着它)。
4.UINavigationController 通过栈来管理视图,UITabBarController 通过数组来管理视图控。
常用属性:
viewControllers:设置 UITabBarController 的子控制器
selectedIndex:属性可用于设置当前被选中的为哪个 Viewcontroller
selectedViewController:设置该属性可以设置当前选中的 ViewController
tabBar:设置 UITabBarController 底部标签栏背景图片、背景颜色、系统图标颜色等信息
tabBarItem:设置 UITabBarController 子控制器的相关标题、图标等信息
delegate:可以获取 TabBar 上点击事件
UITabBarItem⾥面显⽰什么内容,由对应子控制器的 tabBarItem 属性来决定。
UITabBar上面显示的每一个Tab都对应着一个ViewController,我们可以通过设置 Viewcontroller 的 tabBarItem 属性来改变 Tabbar 上对应的tab显示内容。
否则系统将会根据 ViewController 的 title 自动创建一个TabBarItem,该 TabBarItem 只显示文字,没有图标。当我们自己创建 UITabBarItem 的时候,我们可以指定显示的图标和对应的文字。
UITabBar属性:
backgroundImage:设置 TabBar 背景图片
selectionIndicatorImage:设置 TabBar 选中 Item 背景图片
barTintColor:TabBar背景颜色
tintColor:TabBar选重 Item 图标颜色,只有使用系统默认图标时才有效
shadowImage:设置分隔线条
[self.tabBar setBackgroundImage:[UIImageimageNamed:@"TabBar_backGround_01"]];//设置tabBar背景图片
[self.tabBar setSelectionIndicatorImage:[UIImage imageNamed:@"TabBar_backGround"]];//设置tabBar选中item背景图片
[self.tabBar setBarTintColor:[UIColorblackColor]];//设置tabBar背景颜色
[self.tabBar setTintColor:[UIColorredColor]];//设置tabBar选中item图标颜色,只有使用系统默认图标时才有效
UITabBarItem属性:
title:标题文字
image:图标图片
selectedImage:设置选中时图标图片
badgeValue:设置提醒数字
UINavigationBar
UINavigationController 是以栈的方式管理它的视图控制器的,视图控制器的弹入弹出都是入栈、出栈的过程,导航栈就相当于存放 ViewController 的数组。
1、NavigationController 以导航栈来存储 ViewController。
2、NavigationBar 一直存在于界面上方,由 NavigationController 根据 Navigation Stack 中栈顶的ViewController 的内容来管理。
3、Toolbar 则存在于界面下方且默认为隐藏,同样由 NavigationController 根据Navigation Stack中栈顶的 ViewController 的内容来管理。
4、UINavigationController的ContentView里始终显示栈顶控制器的view。
5、NavigationController是一个ViewController容器,它把其他的ViewController的内容嵌套在容器里。 你能看到一个NavigationController的view,它包含了navigation bar、toolbar和一个相当于栈顶ViewController的content view。
UINavigationController常用方法
pushViewController
popViewControllerAnimated
popToViewController
popToRootViewControllerAnimated
setViewControllers
UINavigationController常用属性:
viewControllers:所有处于栈中的控制器
topViewController:位于栈顶的控制器
visibleViewController:当前可见的VC,可能是topViewController,也可能是当前topViewController present(modal)出来的VC,总而言之就是可见的VC。
navigationBar:顶部导航栏
navigationItem:设置导航栏上的按钮
setToolbarHidden:工具条
导航条,UINavigationBar除了能定义自身的样式外,还管理一组UINavigationItem。
UINavigationBar常用属性:
barTintColor:设置导航栏颜色
tintColor:设置导航栏两边按钮颜色
translucent:navigationBar的透明状态,默认是半透明的
barStyle:设置navigationBar的样式
setBackgroundImage: forBarMetrics:设置navigationBar的背景图片
shadowImage:设置分隔线条
UITableView
iOS的UITableView显示单列垂直滚动内容。表格中的每一行都包含一段应用内容。UITableView管理表的基本外观,提供显示实际内容的单元格(对象)。标准单元格配置显示文本和图像的简单组合,但可以定义显示所需内容的自定义单元格。还可以提供页眉和页脚视图,以便为单元格组提供其他信息。
UITableView有两种风格:UITableViewStylePlain和UITableViewStyleGrouped。这两者操作起来其实并没有本质区别,只是后者按分组样式显示前者按照普通样式显示而已。
UITableView是利用NSIndexPath类型确定一个UITableViewCell单元格所在的位置的。NSIndexPath包含两个成员,一个是section,一个是row。section代表的是分组号,row代表的是在该分组下的行号(都从0开始编号)。例如section=1,row=2就代表了第2个分组的第3行。
在加载一个UITableView时,系统会自动调用其UITableViewDataSource代理方法(数据源协议方法),如果UITableView所在的UIViewController没有实现其数据源代理,那么这个iOS程序就会抛出异常。所以要实现UITableViewDataSource协议,具体实现方法详见Objective-C的协议。
在UITableViewDataSource中,规定了一下方法,用于确定UITableView的格式和内容:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
//确定UITableView的分组个数,一般这个方法直接返回分组数即可。
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
//确定第section个分组的标题文本,一般通过switch语句来判断section的值,根据不同section来返回不同的字符串。
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
//确定第section个分组的说明文本,一般通过switch语句来判断section的值,根据不同section来返回不同的字符串。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
//确定第section个分组包含的UITableViewCell单元格个数,一般通过switch语句来判断section的值,根据不同section来返回不同整数。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
//在每个UITableViewCell单元格被加载的时候都要调用一次,在其中要实现UITableViewCell的重用,并且要设置UITableViewCell的各种属性,一般利用两层嵌套的switch语句分别判断indexPath的section和row。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
//在任何一个UITableViewCell单元格被用户点按的时候都会调用,在其中要实现点按不同UITableViewCell的不同行为,包括跳转等,同样地一般利用两层嵌套的switch语句分别判断indexPath的section和row。
UITableViewCell的重用
在Dynamic Prototypes内容模式下,UITableViewCell要求必须进行重用(reuse)。重用方法
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
UIScrollView
UIScrollView是用来在屏幕上显示那些在有限区域内放不下的内容。ScrollView应该首先有一个窗口,用来显示内容,其次,还要有内容本身。这里的这个显示窗口就是UIScrollView,这个窗口可以是整个手机屏幕,也可以只是手机屏幕的一部分区域(屏幕其他部分可以显示些别的东西)。
contentSize描述了有多大范围的内容需要使用scrollView的窗口来显示,其默认值为CGSizeZero,也就是一个宽和高都是0的范围。contentSize的范围是以scrollView的位置为基准的。
contentOffset: 描述了内容视图相对于scrollView窗口的位置(当然是向上向左的偏移量咯)。默认值是CGPointZero,也就是(0,0)。当视图被拖动时,系统会不断修改该值。也可以通过setContentOffset:animated:方法让图片到达某个指定的位置。
scrollRectToVisible:animated:与setContentOffset:animated:类似,只不过是将scrollView坐标系内的一块指定区域移到scrollView的窗口中,如果这部分已经存在于窗口中,则什么也不做。
contentInset: 表示scrollView的内边距,也就是内容视图边缘和scrollView的边缘的留空距离,默认值是UIEdgeInsetsZero,也就是没间距。
回弹机制:bounces alwaysBounceHorizontal alwaysBounceVertical
- bounces:描述的当scrollview的显示超过内容区域的边缘以及返回时,是否有弹性,默认值为YES。值为YES的时候,意味着到达contentSize所描绘的的边界的时候,拖动会产生弹性。值为No的时候,拖动到达边界时,会立即停止。所以,如果在上面的例子当中,将bounces设置为NO时,窗口中是不会显示contentSize范围外的内容的。
- alwaysBounceHorizontal:默认值为NO,如果该值设为YES,并且bounces也设置为YES,那么,即使设置的contentSize比scrollView的size小,那么也是可以拖动的。
- alwaysBounceVertical:默认值为NO,如果该值设为YES,并且bounces也设置为YES,那么,即使设置的contentSize比scrollView的size小,那么也是可以拖动的。
状态条显示:
根据我们的实际需要,我们可以对状态条进行各种设置。
- indicatorStyle: 状态条的风格,默认值为UIScrollViewIndicatorStyleDefault。除此之外,还有UIScrollViewIndicatorStyleBlack, UIScrollViewIndicatorStyleWhite两种其他风格。可以用来和环境配色。
- showsHorizontalScrollIndicator : 当处于跟踪状态(tracking)时是否显示水平状态条,默认值为YES。下一节说明什么是跟踪状态。
- showsVerticalScrollIndicator : 当处于跟踪状态(tracking)时是否显示垂直状态条,默认值为YES。
- scrollIndicatorInsets : 状态条和scrollView边距的距离(暂时还没想明白为什么要有这个)。
- flashScrollIndicators: 短暂的显示一下状态条,当你将scrollView调整到最上面时,需要调用一下该方法。
状态跟踪:
之前提到过跟踪状态(tracking)。相关的属性有三个:tracking dragging decelerate,这三个属性表明了当前视图的滚动状态。
- tracking: 只读,一旦用户开始触摸视图(也许还没有开始拖动),该属性值为YES
- dragging: 只读,当用户开始拖动(手指已经在屏幕上滑动一段距离了),该属性值为YES
- decelerate: 只读,当用户松开手指,但视图仍在滚动时,该值返回YES
- zooming: 只读,用户是否正在进行缩放手势。
- zoomBouncing:只读,当缩放超过最大或者最小范围的时候,回弹到最大最小范围的过程中,该值返回YES。
缩放:
scrollView除了支持拖动之外,还支持缩放。
- maximumZoomScale: 最大放大比例,默认值为1,不得小于minimumZoomScale
- minimumZoomScale: 最小放大比例,默认值为1,不得大于maxmumZoomScale
- bouncesZoom: 描述在缩放超过缩放比例时,是否bounce,默认值为YES。如果值为NO,则达到最大或最小缩放比例时会立即停止缩放。否则,产生弹簧效果。
- zoomScale: 当前的缩放比例。系统会根据缩放过程调整此值。
- setZoomScale:animated:: 程序设置缩放大小。
- zoomToRect:animated: 将内容视图缩放到指定的Rect中。
- panGestureRecognizer
- pinchGestureRecognizer
其他设置
- delegate: scrollView的委托对象,该委托对象必须实现UIScrollViewDelegate协议,这些方法会在合适的时候被调用。
- scrollEnabled:视图是否可被拖动,默认值为YES。一旦该值设置为NO,则scrollView不再接受触屏事件,会直接传递响应链。
- scrollToTop:是否启动“滚动至顶端”手势,默认值为YES。当用户使用了“滚动至顶端”手势(轻击状态栏)时,系统会要求离状态栏最近的scrollView滚动到顶端,如果scrollToTop设置为NO,则该scrollView的delegate的scrollViewShouldScrollToTop:方法会返回NO,不会滚动到顶端。否则,则会滚动到顶端。滚动到顶端之后,会给delegate发送scrollViewDidScrollToTop:消息。需要注意的是,在iphone上,如果有多个scrollview的scrollToTop参数设置为YES的时候,“滚动至顶端”手势会失效。
- delaysContentTouches:是否推迟触屏手势处理,默认值为YES。设置为YES的时候,系统在确定是否发生scroll事件之后,才会处理触屏手势,否则,则会立即调用touchesShouldBegin:withEvent:inContentView:方法。
- directionalLockEnabled:是否锁定某个特定方向的滚动,默认值为NO。设置为YES时,一旦用户向水平或竖直方向拽动时,另一个方向的滚动则被锁定了。但是如果首次拽动是斜着的,那么则不会锁定方向。
- keyboardDismissMode: 当拖动发生时,键盘的消失模式,默认值是不消失。
- pagingEnabled:是否使用分页机制,默认值为NO。当设置为YES时,会按照scrollView的宽度对内容视图进行分页。
- decelerationRate: 手指滑动后抬起,页面的减速速率。可以使用UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast常量值来设置合理的减速速率。
NSURLConnection
作用:
1、负责发送请求,建立客户端和服务器的连接发送数据给服务器
2、并收集来自服务器的响应数据
步骤:
1、创建一个NSURL对象,设置请求路径
2、传入NSURL并创建一个NSURLRequest对象,设置请求头和请求体
3、使用NSURLConnection发送请求
NSURL:收纳请求的地址
NSURLRequest:一个NSURLRequest对象就代表一个请求,它包含的信息有一个NSURL对象、请求方法、请求头、请求体等等
NSMutableURLRequest是NSURLRequest的子类
同步请求例:
-(void)sendSynchronousRequest{
//1、创建一个URL
//协议头+主机地址+接口名称+?+参数1&参数2&参数3
//这里的话是我自己使用.Net开发的一个本地后台接口 http://192.168.1.0:8080/login?username=LitterL&pwd=123
NSURL *url = [NSURL URLWithString:@"http://192.168.1.0:8080/login?username=LitterL&pwd=123"];
//2、创建请求(Request)对象(默认为GET请求);
NSURLRequest *requst = [[NSURLRequest alloc]initWithURL:url];
//3、发送请求
/*
第一个参数:请求对象
第二个参数:响应头
第三个参数:错误信息
返回值:NSData类型,响应体信息
*/
NSError *error = nil;
NSURLResponse *response = nil;
//发送同步请求(sendSynchronousRequest)
NSData *data = [NSURLConnection sendSynchronousRequest:requst returningResponse:&response error:&error];
//如果没有错误就执行
if (!error) {
//打印的服务端返回的信息以及错误信息
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
NSLog(@"%@",error);
}
}
异步请求例:
-(void)sendAsynchronousRequest{
//1、创建一个URL
NSURL *url = [NSURL URLWithString:@"http://192.168.1.0:8080/login"];
//2、创建请求(Request)对象 这里使用的是它的子类NSMutableURLRequest,因为子类才具有设置方法和设置请求体的属性
NSMutableURLRequest *requst = [[NSMutableURLRequest alloc]initWithURL:url];
//2.1、设置请求方法
requst.HTTPMethod = @"POST";
//2.2、设置请求体,因为传入的为Data数据所有这里需要转换
requst.HTTPBody = [@"username=LitterL&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];
//2.3、设置请求超时时间,如果超过这个时间,请求为失败
requst.timeoutInterval = 10;
//3、发送请求
/*
第一个参数:请求对象
第二个参数:队列
第三个参数:Block回调函数
response:响应头
data:响应体信息
connectionError:错误信息
*/
//发送异步请求(sendAsynchronousRequest)
[NSURLConnection sendAsynchronousRequest:requst
queue:[[NSOperationQueue alloc]init]
completionHandler:^(NSURLResponse * _Nullable response,
NSData * _Nullable data,
NSError * _Nullable connectionError) {
NSLog(@"----%@",[NSThread currentThread]);
//解析数据
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
}
NSURLSession
NSURLSession提供了一个可供通过网络下载内容的API,并且具有丰富的代理方法。在iOS中,NSURLSession支持在app未运行或挂起时进行后台下载。此外,NSURLSession原生的支持data、file、ftp、http和https URL方案,以及用户首选项中代理和socks网关。
- Session类型
会话由创建配置对象时调用的方法决定。可以分为以下几类:
- Shared Session:获取单例对象。使用全局共享的会话,并且不能调整cache、cookie和证书等;也不能使用代理方法,因此将不能检测下载进度一类事件。
- Default Session:默认会话配置使用基于磁盘持久缓存,将凭据保存在用户的钥匙串中,默认将cookie存储在与NSURLConnection相同的共享cookie存储中。通过调用NSURLSessionConfiguration类中的defaultSessionConfiguration方法创建默认会话。
- Ephemeral Session:会把cacehe、cookie和用户凭据保存在RAM中而不是用户磁盘中,只有在你主动保存某个URL的内容到某个文件时才会保存数据,因此最大好处是可以保护用户隐私,适用于无痕浏览。可以缓存的数据大小取决于剩余RAM大小,当会话销毁后,所有临时数据均会清除。另外,在iOS中,应用程序挂起时保存在内存中的数据不会被清除,但在应用程序终止或系统内存不足时会被清除。通过调用NSURLSessionConfiguration类中的ephemeralSessionConfiguration创建Ephmeral Session。
- Background Session:允许在后台执行HTTP和HTTPS上传或下载任务。backgroundSessionConfigurationWithIdentifier:配置的会话由系统在单独的进程中控制数据传输。在iOS中,backgroundSessionConfigurationWithIdentifier:配置的会话可以使应用程序挂起或终止后依然进行传输数据。如果app是由系统终止并重新启动,app可以使用相同标志符创建新配置对象和会话,并恢复到终止时的传输状态;如果用户从多任务屏幕中终止app,系统会取消所有后台传输任务,此外,系统也不会重新启动app,必须由用户启动app后传输任务才会继续。通过调用NSURLSessionConfiguration类中的backgroundSessionConfigurationWithIdentifier:创建Background Session。
// 创建Shared Session
NSURLSession *sharedSession = [NSURLSession sharedSession]; // 只能在completionHandler的块中获取数据
// 创建Default Session
NSURLSessionConfiguration *defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultSessionConfiguration delegate:self delegateQueue:nil]; // 在代理方法中获取数据
// 创建Ephemeral Session
NSURLSessionConfiguration *ephemeralSessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralSessionConfiguration]; // 在completionHandler的块中获取数据
// 创建Background Session
NSURLSessionConfiguration *backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"identifier"];
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration]; // 在completionHandler的块中获取数据
- Task类型
在会话中,创建任务用于上传数据到服务器,然后从服务器取回文件到本地磁盘或NSData对象到内存中,NSURLSessionAPI提供三种Task类型。
- Data Task:发送和接收NSData类型数据,一般用于交互式请求。
- Upload Task:继承自Data Task,一般以文件格式传输数据。支持app未运行时上传数据。
- Download Task:取回的数据是文件格式,支持app未运行时上传、下载。
和其他网络请求API一样,NSURLSession也是异步的,根据你Session配置的不同,以下面两种方式之一返回数据。
从completionHandler的块中获取数据,当传输完成或失败后调用块。
在代理方法中获取数据。有多种代理方法可供选择使用,比上面的方法功能强大些,例如可以检测下载进度。
- NSURLSession的使用步骤
(1)创建会话配置。对于Background Session必须包含一个单独的标志符,标志符不能为nil或空字符串,用于在app崩溃、终止或挂起后将会话再次匹配。
(2)选择会话配置类型,创建会话。
(3)创建任务。根据想要实现的功能选择Task类型。
(4)执行任务。所有创建的任务默认都是挂起状态,必须调用resume方法任务才会执行。
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"https://itunes.apple.com/search?term=apple&media=software"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"%@",json);
}];
[dataTask resume];
}
git
一、本地管理
1. 提交操作
(1). 配置全局用户名和邮箱
配置用户名 : git config --global user.name "liwenqi08"
配置邮箱 : git config --global user.email "[email protected]"
获取全局配置信息 : git config --list
(2). 创建仓库
cd到你想要创建仓库的地址,运行 git init ,根目录下多了一个.git的文件夹,本地仓库就已经建好了
(3). 忽略文件
有时候有些文件我们不想每次提交,那么我们可以配置.gitignore,并把它放到根目录,忽略该文件的效果。eg:在里面添加 .DS_Store,则不会对该文件的修改加入到工作区中。有些已经跑过的项目,你会发现你在.gitignore加入 xcuserdata/ 后,UserInterfaceState.xcuserstate这个文件还是频繁更新,扰人至极,去掉这个文件的具体步骤如下:
到目录下把这个文件直接干掉,然后commit这次修改,重启Xcode就好了。
或者使用命令行:
git rm --cached 该文件路径
git commit -m "Removed stupid file"
(4). 查看仓库的当前状态
git status 查看当前文件状态
git diff 查看当前改动
git diff HEAD -- filename 查看该文件工作区与当前版本库的差别
(5). 查看之前的所有log
git log 查看详细改动
git log --pretty=oneline 简单方式查看版本改动
(6). 从工作区添加到暂存区
git add filename.txt 添加单个文件
git add *.txt 添加所有txt文件
git add . 添加所有文件
(7). 丢弃工作区中的修改
git checkout -- filename 丢弃对应文件的修改
git checkout head . 丢弃所有工作区的修改
(8). 将暂存的修改还原到工作区
git reset head 或者 git reset head filename
(9). 将暂存区的改动上传到仓库
git commit -m "commitSomething"
(10). 版本回归
git reset --mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息
git reset --soft:回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可
Git reset --hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容
git reset --hard HEAD^ 回滚到上一个版本
在没关闭命令行的前提下,找到上次git log的日志,找到对应的版本号,输入git reset --hard 最后一条数据回来了。已经关了命令行就执行 git reflog
2. 分支管理
(1). 创建分支
git checkout -b branchName (相当与: git branch branchName git checkout branchName两句命令)
git branch branchName 在指定节点创建分支
(2). 查看当前所有分支以及状态
git branch
(3). 合并分支
git merge branchName 用于合并指定分支到当前分支
(4). 删除分支
git branch -d branchName 删除指定分支
二、远程管理
首先登陆github创建一个远程仓库,获取到远程仓库的地址。(https://git.oschina.net/yang_shuai/gitTest.git)
(2). 克隆远程仓库
git clone https://git.oschina.net/yang_shuai/gitTest.git
(2). 查看本地远程仓库状态
git remote -v
(3). 链接远程仓库
git remote add origin https://git.oschina.net/yang_shuai/gitTest.git
(4). 清除远程仓库
git remote rm origin
(5). 获取远程库数据单不合并
git fetch origin
(6). 将本地分支推送到远程分支并合并
git push origin master:master master为分支名,前面的master为本地分支名,后面的master为远程分支名
(7). 获取并合并远程分支的两种方法
第一种: 使用git fetch 获取到当前最新的远程分支,然后使用git merge origin/master 进行合并。
第二种:直接使用 git pull origin master:master 获取并直接合并到对应分支。
针对两种获取合并方式,个人更推崇第一种,首先可以知道队友都改了些什么,另外可以准确的进行一对多的合并。
三、冲突解决
目前有两种解决冲突的方式。
第一种:修改当前文件,也就是说在你编辑的文件中直接修改然后git add . ,git commit -m “mergeok”,手动保留你想要的修改。
第二种:自动解决冲突的工具
四、标签操作
git tag v1.0.0 (后面可加版本号在固定节点打上tag)
git tag 查看所有标签。
git tag -d v1.0.0 删除名称为v1.0.0的本地标签
git push origin :refs/tags/v1.0.0 删除名称为v1.0.0的远程仓库标签
vim
i → Insert 模式,按 ESC 回到 Normal 模式.
x → 删当前光标所在的一个字符。
:wq → 存盘 + 退出 (:w 存盘, :q 退出)
dd → 删除当前行,并把删除的行存到剪贴板里
p → 粘贴剪贴板
hjkl (强例推荐使用其移动光标,但不必需) →你也可以使用光标键 (←↓↑→). 注: j 就像下箭头。
:help → 显示相关命令的帮助。
//各种插入模式
a → 在光标后插入
o → 在当前行后插入一个新行
O → 在当前行前插入一个新行
cw → 替换从光标所在位置后到一个单词结尾的字符
//简单的移动光标
0 → 数字零,到行头
^ → 到本行第一个不是blank字符的位置(所谓blank字符就是空格,tab,换行,回车等)
$ → 到本行行尾
g_ → 到本行最后一个不是blank字符的位置。
/pattern → 搜索 pattern 的字符串
拷贝/粘贴 (陈皓注:p/P都可以,p是表示在当前位置之后,P表示在当前位置之前)
P → 粘贴
yy → 拷贝当前行当行于 ddP
Undo/Redo
u → undo
→ redo
//打开/保存/退出/改变文件(Buffer)
:e → 打开一个文件
:w → 存盘
:saveas → 另存为
:x, ZZ 或 :wq → 保存并退出 (:x 表示仅在需要时保存,ZZ不需要输入冒号并回车)
:q! → 退出不保存 :qa! 强行退出所有的正在编辑的文件,就算别的文件有更改。
:bn 和 :bp → 你可以同时打开很多文件,使用这两个命令来切换下一个或上一个文件。
CocoaPods
CocoaPods使用
Reveal
Reveal使用