在简书看到大牛的知识点,发现很多知识点自己一知半解,能做项目但理论不够扎实,默默地去百度总结一下.放到这里和大家交流交流.
如有侵权,告知即删!
01.用StoryBoard开发界面有什么弊端?如何避免?
优点:
开发界面所见即所得,可以快速通过拖拽构造界面。
你可以从 storyboard 中很方便地梳理出所有View Controller的界面间的调用关系。这一点对于新加入项目组的开发同事来说,比较友好。
缺点:
xib 对版本管理是灾难。storyboard 实际上的多个 xib 的集合,所以更容易让多人编辑产生冲突。而虽然它们是 xml 格式,但是冲突解决起来还是不如代码那么容易。
苹果对 xib, storyboard 的设计中带有当前电脑的操作系统版本和 Xcode 版本。所以如果两个协作的开发者电脑操作系统或 Xcode 有不一样的话,每次打开必定会修改这个文件。另外即使操作系统版本和 Xcode 版本一样,有些时候打开看也会造成一些自动的修改。
xib 和 storyboard 不太方便做界面的模块化管理,比如我们想统一修改界面中所有按钮的字体样式,那么在 xib 和 storyboard 只能一个一个手工修改,而如果是代码编写的,则只需要改一个工厂方法的实现即可。
对于复杂的 App,storyboard 的性能会比较差。
02.进程和线程的区别?同步异步的区别?并行和并发的区别?
先简单说说线程与进程的概念:
(1)进程是指一个内存中运行的应用程序,比如在Windows系统中,一个运行的exe就是一个进程。
(2)线程是指进程中的一个执行流程。
区别:
一个程序至少有一个进程,而一个进程至少有一个线程。一个应用程序可以同时启动多个进程。例如对于IE浏览器程序,每打开一个IE浏览器窗口,就启动了一个新的进程。而线程则是指进程中的一个执行流程,一个进程可以有多个线程,每个线程分别执行不同的任务,当进程内的多个线程同时运行时,这种运行方式就被称为并发运行。
另外,线程与进程还有一个非常重要的区别:每个进程在执行过程中都拥有独立的内存单元,而同一个进程中的多个线程则共享内存,从而极大地提高了程序的运行效率。
1.并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥
2.互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。
3.同步:进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。
其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。
4.并行:在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。
5.多线程:多线程是程序设计的逻辑层概念,它是进程中并发运行的一段代码。多线程可以实现线程间的切换执行。
6.异步:异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。
03.线程间通信?
线程间通信常用的方法
-
NSThread
可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5,2_0);
用法如下://点击屏幕开始执行下载方法
- (void)touchesBegan:(NSSet*)touches withEvent: (UIEvent*)event {
[self performSelectorInBackground:@selector(download) withObject: nil];
}
//下载图片
- (void)download{
// 1.图片地址NSString*urlStr =@"http://d.jpg";
NSURL*url = [NSURL URLWithString: urlStr];
// 2.根据地址下载图片的二进制数据
NSData*data = [NSData dataWithContentsOfURL: url];
NSLog(@"---end");
// 3.设置图片
UIImage*image = [UIImage imageWithData: data];/
/ 4.回到主线程,刷新UI界面(为了线程安全)
[self performSelectorOnMainThread: @selector(downloadFinished:) withObject: image waitUntilDone:NO];
// [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject: image waitUntilDone:YES];
}
- (void)downloadFinished:(UIImage*)image{
self.imageView.image = image;NSLog(@"downloadFinished---%@", [NSThread currentThread]);
}
GCD一个线程传递数据给另一个线程,如:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片
NSURL*url = [NSURLURLWithString:@"http://d.jpg"];
NSData*data = [NSData dataWithContentsOfURL: url];
UIImage*image = [UIImage imageWithData: data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
[self.button setImage: image forState:UIControlStateNormal];
});
});
}
04.GCD的一些常用的函数?(group,barrier,信号量,线程同步)
GCD中实现线程同步的方式
dispatch_group
dispatch_group是GCD中经常使用的线程同步方式,具体用法如下:
dispatch_queue_t queue = dispatch_queue_create("cc.imguiqing", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{ NSLog(@"task 1 on %@",[NSThread currentThread]);});
dispatch_group_async(group, queue, ^{ NSLog(@"task 2 on %@",[NSThread currentThread]);});
dispatch_group_async(group, queue, ^{ NSLog(@"task 3 on %@",[NSThread currentThread]);
});
可以看出,这个和普通的GCD任务相比,每个API都多了一个group参数.但是如果仅仅是像上面的方式使用,就没有什么必要了.我们使用Group的原因,更多是想要知道这个Group中的执行情况.借此来获得时机做一些逻辑操作.所以dispatch_group提供了两个API:
通知Group中的任务都执行完毕
dispatch_group_notify(group, queue, ^{ NSLog(@"all task done");});
阻塞式的等待Group中的任务都执行完毕
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(@"since all done , I move on");
更常见的写法
上面的写法虽然简单,但是如果看过一些三方库的代码,发现那么用的并不多.更多的是利用dispatch_group_enter(group)和dispatch_group_leave(group)来包装任务,本质上两者没有区别,多说这些仅仅是让你别以后看代码的时候感到疑惑,
代码如下:
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_group_t group = dispatch_group_create();dispatch_group_enter(group);dispatch_async(queue, ^{
NSLog( @"task 1 --- %@", [NSThread currentThread] );
dispatch_group_leave(group);} );
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog( @"task 2 --- %@", [NSThread currentThread] );dispatch_group_leave(group);} );
dispatch_group_notify(group, queue, ^{
NSLog( @"all task done %@", [NSThread currentThread] );
} );
信号量
信号量可以理解为一个特殊的变量.程序对它的访问都是原子性的,我们通过PV操作来修改信号量.
使用代码简单说明:
dispatch_semaphore_t sem =dispatch_semaphore_create(0);
[networkManager requestWithDelay:5completion:^{dispatch_semaphore_signal(sem);//+1}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
//-1NSLog(@"five sectonds");
信号量创建的时候, 可以给他指定一个值.dispatch_semaphore_signal(sem)对信号进行+1操作.dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)对信号进行-1操作.当进行-1时,如果发现信号结果会小于0,那么线程进入阻塞状态.只有当信号>=0才能通过.
那么上面的代码段就容易明白了: 一直等到一个异步的网络请求结束,才继续执行NSLog(@"five sectonds");,也是就其他的逻辑
Barrier
相比上面两种方式,Barrier知道的人相对少一些.但是Barrier用起来相对上面两种更加简单.
dispatch_queue_t queue = dispatch_queue_create("cc.imguiqing", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue,^{
NSLog(@"task 1 on %@",[NSThread currentThread]);});
dispatch_async(queue,^{
NSLog(@"task 2 on %@",[NSThread currentThread]);});
dispatch_barrier_async(queue,^{
NSLog(@"barrier ==========");});
dispatch_async(queue,^{
NSLog(@"task 3 on %@",[NSThread currentThread]);
});
上面的代码,task 1和task 2会并发执行,然后执行barrier,最后是task 3,用图来说明:
[图片上传中。。。(1)]
这个 barrier就相当于一个栅栏,将不同的任务区分开来.从代码中也不难看出,这个barrier函数不需要依赖其它的变量,没有侵入性.所以非常好用.和Group也是非常好搭配.例如下面的代码:
dispatch_queue_t queue = dispatch_queue_create("cc.imguiqing", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (int i =0; i < 500000000; i++) {}
NSLog(@"task 1 on %@",[NSThread currentThread]);});
dispatch_barrier_async(queue,^{
NSLog(@"======");});
dispatch_group_async(group, queue, ^{
for (int i =0; i < 500000000; i++) {}
NSLog(@"task 2 on %@",[NSThread currentThread]);});
dispatch_barrier_async(queue,^{
NSLog(@"======");});
dispatch_group_async(group, queue, ^{
NSLog(@"task 3 on %@",[NSThread currentThread]);});
dispatch_group_notify(group, queue, ^{
NSLog(@"all task done");});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
NSLog(@"since all done , I move on");
能保证task 1 2 3顺序执行,同时,由于使用了Group,也能知道执行结束的时机. 但是仅仅是为了说明问题,如果要顺序执行,那么还是使用GCD中同步队列更加合适.
注意点: 这个barrier函数只能用于并发队列,且不能是global queue.