简述OC语言


对于一门语言的学习是需要时间领悟的,而对于一些原理性的问题,我们需要清楚其核心思想,知其然而知其所以然,这样才能有利于自己的后续发展。本文只是简述,没有面面具到。


OC是在C语言的基础上加入了一些面向对象的特性(封装,继承,多态),OC完全兼容C语言,在代码中,可以混用C,甚至是C++代码(C++是在C语言的基础上开发的一种面向对象编程语言)。

抽象是面向对象的思想基础,抽象包括两个方面,一是过程抽象,二是数据抽象,抽象是一种思想,封装继承和多态是这种思想的实现。

封装

封装是把过程和数据包围起来,有限制的对数据的访问。

继承

继承是一种层次模型,允许和鼓励类的重用,继承性很好的解决了软件的可重用性问题。但是,不恰当地使用继承导致的最大的一个缺陷特征就是高耦合(有时候也是一种需求),恰当使用继承可以达到层级关系明显,功能划分清晰。

多态

其本质是子类通过覆盖或重载父类的方法,来使得对同一类对象同一方法的调用产生不同的结果。

消息传递

消息传递模型(Message Passing)是Objective-C语言的核心机制。在Objective-C中,没有方法调用这种说法,只有消息传递。在C++或Java中调用某个类的方法,在Objective-C中是给该类发送一个消息。在C++或Java里,类与类的行为方法之间的关系非常紧密,一个方法必定属于一个类,且于编译时就已经绑定在一起,所以你不可能调用一个类里没有的方法。而在Objective-C中就比较简单了,类和消息之间是松耦合的,方法调用只是向某个类发送一个消息,该类可以在运行时再确定怎么处理接受到的消息。也就是说,一个类不保证一定会响应接收到的消息,如果收到了一个无法处理的消息,那么程序就是简单报一个错。甚至你可以向一个值为nil的空对象发送消息,系统都不会出错或宕掉。这种设计本身也比较符合软件的隐喻。

动态性(Runtime)

Objective-C 是面相运行时的语言,它会尽可能的把编译和链接时要执行的逻辑延迟到运行时。使用Runtime可以按需要把消息重定向给合适的对象,交换方法的实现等等。 Runtime简称运行时,其中最主要的是消息机制,是一个主要使用 C 和汇编写的库,为 C 添加了面相对象的能力并创造了 Objective-C。OC的函数调用称为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。 如:

1
2
[obj makeText];
objc_msgSend(obj,@selector(makeText));

编译器执行上述转换。在objc_msgSend函数中,首先通过obj的isa指针找到obj对应的class。每个对象内部都默认有一个isa指针指向这个对象所使用的类。isa是对象中的隐藏指针,指向创建这个对象的类。在Class中先去cache中通过SEL查找对应函数method(cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若cache中未找到,再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

OC的动态性的三方面

动态类型、动态绑定、动态加载(动态是因为 到运行时(runtime)才会做一些事情)

动态类型:就是id类型;

动态绑定:需要用到@selector/SEL,SEL并不是C里面的函数指针,SEL变量只是一个整数,他是该方法的ID。我们是根据一个ID整数来查找方法,不仅方便,而且效率更高。

动态加载:就是根据需求动态地加载资源,在运行时加载新类。

在运行时创建一个新类,只需要3步:

1、为 class pair分配存储空间 ,使用 objc_allocateClassPair函数

2、增加需要的方法使用class_addMethod函数,增加实例变量用class_addIvar

3 、用objc_registerClassPair函数注册这个类,以便它能被别人使用。

强大的Method Swizzling(方法交叉使用)

在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP类似函数指针,指向具体的Method实现。

method_exchangeImplementations 来交换2个方法中的IMP, 用 class_replaceMethod 来修改类, 用 method_setImplementation 来直接设置某个方法的IMP,归根结底,都是偷换了selector的IMP。

RunLoop

RunLoop是一让线程能随时处理事件但不退出的机制。

RunLoop的四个作用为:

1、使程序一直运行接受用户输入;

2、决定程序在何时应该处理哪些Event;

3、调用解耦;

4、节省CPU时间。

主线程的runloop默认是启动的。 Cocoa中的NSRunLoop类并不是线程安全的。 对其它线程来说,runloop默认是没有启动的。 Runloop在你要和线程有更多的交互时才需要,比如以下情况:

1.使用端口或自定义输入源来和其他线程通信;

2.使用线程的定时器;

3.Cocoa中使用任何performSelector…的方法;

4.使线程周期性工作;

事件响应链

对于IOS设备用户来说,操作设备的方式主要有三种:触摸屏幕、晃动设备、通过遥控设施控制设备。

对应的事件类型有以下三种:

1、触屏事件(Touch Event)

2、运动事件(Motion Event)

3、远端控制事件(Remote-Control Event)

传递链:由系统向离用户最近的view传递。

响应链:由离用户最近的view向系统传递。

UIResponder是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。我们熟悉的UIApplication、 UIViewController、UIWindow和所有继承自UIView的UIKit类都直接或间接的继承自UIResponder,所以它们的实例都是可以构成响应者链的响应者对象。

响应者链有以下特点:

1、响应者链通常是由视图(UIView)构成的;

2、一个视图的下一个响应者是它视图控制器(UIViewController)(如果有的话),然后再转给它的父视图(Super View);

3、视图控制器(如果有的话)的下一个响应者为其管理的视图的父视图;

4、单例的窗口(UIWindow)的内容视图将指向窗口本身作为它的下一个响应者,Cocoa Touch应用不像Cocoa应用,它只有一个UIWindow对象,因此整个响应者链要简单一点;

5、单例的应用(UIApplication)是一个响应者链的终点,它的下一个响应者指向nil,以结束整个循环。

生命周期

1、application didFinishLaunchingWithOptions:当应用程序启动时执行,应用程序启动入口,只在应用程序启动时执行一次。若用户直接启动,lauchOptions内无数据,若通过其他方式启动应用,lauchOptions包含对应方式的内容。

2、applicationWillResignActive:在应用程序将要由活动状态切换到非活动状态时候,要执行的委托调用,如 按下 home 按钮,返回主屏幕,或全屏之间切换应用程序等。

3、applicationDidEnterBackground:在应用程序已进入后台程序时,要执行的委托调用。

4、applicationWillEnterForeground:在应用程序将要进入前台时(被激活),要执行的委托调用,刚好与applicationWillResignActive 方法相对应。

5、applicationDidBecomeActive:在应用程序已被激活后,要执行的委托调用,刚好与applicationDidEnterBackground 方法相对应。

6、applicationWillTerminate:在应用程序要完全推出的时候,要执行的委托调用,这个需要要设置UIApplicationExitsOnSuspend的键值。

初次启动: iOS_didFinishLaunchingWithOptions iOS_applicationDidBecomeActive

按下home键: iOS_applicationWillResignActive iOS_applicationDidEnterBackground

点击程序图标进入: iOS_applicationWillEnterForeground iOS_applicationDidBecomeActive

当应用程序进入后台时,应该保存用户数据或状态信息,所有没写到磁盘的文件或信息,在进入后台时,最后都写到磁盘去,因为程序可能在后台被杀死。释放尽可能释放的内存。 - (void)applicationDidEnterBackground:(UIApplication *)application 方法有大概5秒的时间让你完成这些任务。如果超过时间还有未完成的任务,你的程序就会被终止而且从内存中清除。

如果还需要长时间的运行任务,可以在该方法中调用

1
2
3
[application beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"begin Background Task With Expiration Handler");
}];