(1)线程和进程的区别
进程是系统进行资源分配和调度的一个独立单位,线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程基本不拥有系统资源,拥有自己的栈空间,它与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程。同一个进程中的多个线程之间可以并发执行。
进程和线程的主要差别在于他们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,线程知识一个进程中的不同执行路径,有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
一个程序至少有一个进程,一个进程至少有一个线程。
线程的划分尺度小于进程,使得多线程程序的并发性高,多个线程共享内存,极大提到了程序的运行效率。
线程不能独立执行,必须已存在应用程序中,由应用程序提供多个线程执行控制。
进程是资源分配的基本单位,线程是调度的基本单位,因为线程小,调度开销就小,所以效率高。
多线程的意义在于一个应用程序中,有多个执行部分可以同时之星。但操作系统并没有将多个线程看做多个独立的应用
线程执行开销小,但不利于资源的管理和保护
(2)定义一个NSString类型属性时,为什么用copy不用strong?
这个NSString类型属性是一个指针,定义成copy,操作是拷贝一份等同的对象,这个指针指向新生成的拷贝对象。当使用copy时,这个拷贝的对象无论是拷贝自NSString还是NSMutableString结果都是不可变的NSString。而如果用Strong,则指向一个字符串对象,若指向的是一个NSMutableString,则当指向的对象改变时,属性值也会发生相应改变,导致错误,因为是一个不可变字符串
(3)应用多线程的时候会出现什么问题,应如何避免问题的发生?
多线程容易导致资源争抢,发生死锁现象.死锁通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在相互等待,造成了无法执行的情况。
避免死锁的一个通用的经验法则是:当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C
采用GCD中的栅栏方法,用并行队列去装载事件并异步去执行。
(4)应用循环是怎么产生的,如何解决引用循环
两个类中有属性分别为彼此的实例,这样就会引发循环应用。
使用block、NSTimer时也很容易导致循环应用.设置一个应用为弱引用既可以解决强应用循环。
(5)NSTimer使用中的注意事项
在类中定义定时器并把目标对象设置为self时,所以NSTimer实例会保存此实例,但定时器是用在类中用实例变量存放的,所以此实例也保留了定时器,这就造就了循环应用。除非调用invalidate方法并把定时器设置为nil,或者系统回收实例,才能打破循环应用,如果无法确保stop一定被调用,就极易造成内存泄露。
使用block可以防止NSTimer导致的内存泄露.
在NSTimer分类中定义方法,让NSTimer的target为NSTimer实例本身,然后block从类实例中copy一份,在NSTimer中定义方法去执行拷贝的block。在uerInfo中去放block的拷贝。
(6)App都有哪些运行状态?
状态:未运行,未激活,激活,后台,挂起。
未运行:程序未启动
未激活:程序在前台运行,不过没有接受到事件。
激活:程序在前台运行且受到了事件。
后台:程序在后台而且能执行代码,大多程序进入这个状态后会在这个状态停留一会,时间到之后会进入挂起状态。
挂起:程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中,系统内存低时,系统就把挂起的程序清除掉。
各个程序运行状态时代理的回调:
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
告诉代理进程启动但还没进入状态保存
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
告诉代理启动基本完成程序准备开始运行
- (void)applicationWillResignActive:(UIApplication *)application
当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了
- (void)applicationDidBecomeActive:(UIApplication *)application
当应用程序入活动状态执行,这个刚好跟上面那个方法相反
- (void)applicationDidEnterBackground:(UIApplication *)application
当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可
- (void)applicationWillEnterForeground:(UIApplication *)application
当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反。
- (void)applicationWillTerminate:(UIApplication *)application
当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。这个需要要设置UIApplicationExitsOnSuspend的键值。
- (void)applicationDidFinishLaunching:(UIApplication*)application
当程序载入后执行
(7)栈、堆、静态区域的区别
OC中,非对象的变量都存在栈中,对象都存在堆中,静态区域的内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存效率高,内存容量有限。
堆区:OC对象存储于堆中,当对象的应用计数为0时自动释放该对象。
(8)子视图超出父视图的部分能看到么?超出的部分有什么影响?
子视图超出父视图的部分能看到。但是超出的部分不能响应事件
想让超出的部分响应事件,就该写父视图的hitTest方法。判断触碰区域是否在子视图内,如果在子视图内,则返回子视图。让子视图去响应事件。
(9)respond链是如何响应的,响应顺序是怎样的?
在iOS系统中,能够响应并处理事件的对象称之为responder object, UIResponder是所有responder对象的基类,在UIResponder类中定义了处理各种事件,包括触摸事件(Touch Event)、运动事件(Motion Event)和远程控制事件(Remote-Control Events)的编程接口,
UIApplication, UIViewController,UIView和所有继承自UIView的UIKit类(包括UIWindow,继承自UIView)都直接或间接的继承自UIResponder,所以它们的实例都是responder object对象,都实现了上述4个方法。UIResponder中的默认实现是什么都不做,但UIKit中UIResponder的直接子类(UIView,UIViewController…)的默认实现是将事件沿着responder chain继续向上传递到下一个responder,即nextResponder。所以在定制UIView子类的上述事件处理方法时,如果需要将事件传递给next responder,可以直接调用super的对应事件处理方法,super的对应方法将事件传递给next responder,即使用[super touchesBegan:touches withEvent:event];不建议直接向nextResponder发送消息,这样可能会漏掉父类对这一事件的其他处理。
[self.nextResponder touchesBegan:touches withEvent:event];
另外,在定制UIView子类的事件处理方法时,如果其中一个方法没有调用super的对应方法,则其他方法也需要重写,不使用super的方法,否则事件处理流程会很混乱。
responder chain是一系列连接的responder对象,通过responder对象可以将处理事件的责任传递给下一个,更高级的对象,即当前responder对象的nextResponder。
iOS中responder chain的结构为:
第一响应者是第一个接收事件的View对象,我们在Xcode的Interface Builder画视图时,可以看到视图结构中就有First Responder。这里的First Responder就是UIApplication了。另外,我们可以控制一个View让其成为First Responder,通过实现 canBecomeFirstResponder方法并返回YES可以使当前View成为第一响应者,或者调用View的becomeFirstResponder方法也可以,例如当UITextField调用该方法时会弹出键盘进行输入,此时输入框控件就是第一响应者。
iOS系统在处理事件时,通过UIApplication对象和每个UIWindow对象的sendEvent:方法将事件分发给具体处理此事件的responder对象(对于触摸事件为hit-test view,其他事件为first responder),当具体处理此事件的responder不处理此事件时,可以通过responder chain交给上一级处理。
如果hit-test view或first responder不处理此事件,则将事件传递给其nextResponder处理,若有UIViewController对象则传递给UIViewController,传递给其superView。
如果view的viewController也不处理事件,则viewController将事件传递给其管理view的superView。
视图层级结构的顶级为UIWindow对象,如果window仍不处理此事件,传递给UIApplication.
若UIApplication对象不处理此事件,则事件被丢弃。
小结下,使用响应链,能够让一条链上的多个对象对同一事件做出响应。每一个应用有一个响应者链,我们的视图结构是一个N叉树(一个视图可以有多个子视图,一个子视图同一时刻只有一个父视图),而每一个继承自UIResponder的对象都可以在这个N叉树中成为一个节点。当叶节点成为最高响应者的时候,从这个叶节点开始往其父节点开始追溯出一条链,那么对于这一个叶节点来讲,这一条链就是当前的响应者链。响应者链将系统捕获到的UIEvent与UITouch从叶节点层层向上分发,期间可以选择停止分发,也可以继续向上分发。一句话就是事件的传递过程。
(10)GCD中栅栏机制
栅栏函数 只能用在调度并发队列中,不能使用在全局并发队列中.
1.实现高效率的数据库访问和文件访问
2.避免数据竞争dispatch_barrier_async函数会等待追加到并行队列上的并行执行的处理全部结束之后,再将制定的处理追加到该并行队列中。然后在由dispatch_barrier_async函数追加的处理执行完毕后,并行队列才恢复为一般的动作,追加到该并行队列的处理又开始执行。
网友评论