ios高级开发之多线程(三)GCD技术
GCD是基于C的API,它是libdispatch的的市场名称。而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持。
那么我们为什么要用GCD技术呢?
1.GCD能够推迟昂贵的计算任务,并在后台运行它们来改善你的应用的性能。
2.GCD提供一个易于使用的并发模型而不仅仅是锁和线程。以帮助我们避开并发陷阱。
3.GCD具有在常见模式(比如单例)上用更高性能的原语优化你的代码的潜在能力。
4.GCD旨在替换NSTread等线程技术。
5.GCD可充分利用设备的多核。
6.GCD可自动管理线程的生命周期。
说了这些GCD的优点,那么在实际开发中,如何使用GCD来更好满足我们的需求呢?
一、Synchronous&Asynchronous 同步&异步
1.Synchronous同步:同步任务的执行的方式:在当前线程中执行,必须等待当前语句执行完毕,才会执行下一条语句。
来看下同步的代码:
//同步的打印顺序
-(void)syncTask{
NSLog(@"begin");
//GCD的同步方法
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
//任务中要执行的代码
[NSThread sleepForTimeInterval:2.0];
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"end");
}
来看打印出来的:
2019-03-29 12:00:54.993042+0800 wftest[5191:88411] begin
2019-03-29 12:00:56.994525+0800 wftest[5191:88411] <NSThread: 0x600000ff5380>{number = 1, name = main}
2019-03-29 12:00:56.994799+0800 wftest[5191:88411] end
可以看到,即使线程休眠了2秒,他依然会按照顺序执行,等代码块内的代码执行完毕后,才会执行end.
接着我们再来看异步,不在当前线程中执行,不用等当前语句执行完毕,就可以执行下一条语句
来看代码:
//异步顺序
-(void)asyncTask{
//异步不会在当前线程执行,首先需要开辟新的线程,而开辟新的线程也需要一定的时间
NSLog(@"begin");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"end");
}
我们来看下打印的情况:
2019-03-29 16:06:34.600530+0800 wftest[1336:19777] begin
2019-03-29 16:06:34.600763+0800 wftest[1336:19777] end
2019-03-29 16:06:34.600892+0800 wftest[1336:20090] <NSThread: 0x600000acc600>{number = 3, name = (null)}
可以看到,打印出来begin后,直接打印出了end.然后才执行了异步块里的代码。
接下来,我们来看看串行队列Serial queues,和并行队列(并发队列)Concurrent queues.
1.串行队列的特点:
以先进先出的方式执行,顺序调度队列中的任务执行。
无论队列中的任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务。
我们先来看看,串行队列的同步代码:
//串行队列同步函数(在一个线程中执行,注意,GCD的是C语言API,不要和OC弄混)
-(void)serialSync{
//这里有两个参数,第一个参数的标识符,一般为公司域名倒写,第二个参数队列类型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT为并发队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
//创建任务
void (^task1) (void) = ^(){
NSLog(@"task1---%@",[NSThread currentThread]);
}; void (^task2) (void) = ^(){
NSLog(@"task2 -- %@",[NSThread currentThread]);
}; void (^task3) (void) = ^(){
NSLog(@"task3 -- %@",[NSThread currentThread]);
}; //添加任务到队列,同步执行方法
dispatch_sync(serialQueue, task1);
dispatch_sync(serialQueue, task2);
dispatch_sync(serialQueue, task3);
}
然后来看下NSLog打印的东西:
2019-03-29 17:08:10.362074+0800 wftest[2989:50297] task1---<NSThread: 0x600002009340>{number = 1, name = main}
2019-03-29 17:08:10.364550+0800 wftest[2989:50297] task2 -- <NSThread: 0x600002009340>{number = 1, name = main}
2019-03-29 17:08:10.365860+0800 wftest[2989:50297] task3 -- <NSThread: 0x600002009340>{number = 1, name = main}
可以看到task1,taks2,task3是完全按照顺序执行的。
再来看串行队列的异步方法:
先来看代码
//串行队列异步函数
-(void)serialAsync{
//创建一个串行队列
dispatch_queue_t serialQuene = dispatch_queue_create("com.feng", DISPATCH_QUEUE_SERIAL);
//2.创建任务
void (^task1)(void) = ^(){
NSLog(@"task1 --- %@",[NSThread currentThread]);
};
void (^task2)(void) = ^(){
NSLog(@"task 2-- %@",[NSThread currentThread]);
};
void (^task3)(void) = ^(){
NSLog(@"task 3 -- %@",[NSThread currentThread]);
}; //3.添加任务队列
dispatch_async(serialQuene, task1);
dispatch_async(serialQuene, task2);
dispatch_async(serialQuene, task3);
}
来看下打印结果:
2019-03-30 14:27:21.730761+0800 wftest[4929:84017] 主线程 -- <NSThread: 0x600000650540>{number = 1, name = main}
2019-03-30 14:27:21.731252+0800 wftest[4929:84090] task1 --- <NSThread: 0x600000638340>{number = 3, name = (null)}
2019-03-30 14:27:21.731536+0800 wftest[4929:84090] task 2-- <NSThread: 0x600000638340>{number = 3, name = (null)}
2019-03-30 14:27:21.731691+0800 wftest[4929:84090] task 3 -- <NSThread: 0x600000638340>{number = 3, name = (null)}
可以看到,串行队列异步执行,仍然按顺序执行的。也就是说,只要是串行队列,无论是异步,还是同步函数,都是按顺序执行的。
2.看完串行队列,我们来看并发(并行)队列。
并发队列的特点:
1.以先进先出的方法,并发调度队列中的任务的执行。
2.如果是并发队列的同步执行,就会等先被调度的任务执行完毕后,再执行下一个任务。
3.如果是并发队列的异步执行,同时底层线程池有可用的线程资源,会在新的任务调度后,调度下一个任务。
也就是说,先加进来的任务会先被执行,但不用等他执行完毕,就可以接着调度下一个任务。
那么,我们先来看并发队列的同步任务的代码:
//并发队列同步函数
-(void)concurrentSync{
//1.创建并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT); //2.创建任务
void (^task1) (void) = ^(){
NSLog(@"task1 -- %@",[NSThread currentThread]);
};
void (^task2) (void) = ^(){
NSLog(@"task2 -- %@",[NSThread currentThread]);
};
void (^task3) (void) = ^(){
NSLog(@"task3 -- %@",[NSThread currentThread]);
}; //3.添加同步任务到并发队列
dispatch_sync(concurrentQueue, task1);
dispatch_sync(concurrentQueue, task2);
dispatch_sync(concurrentQueue, task3);
}
再来看打印情况:
2019-03-30 14:01:51.358796+0800 wftest[4073:69627] 主线程 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.359220+0800 wftest[4073:69627] task1 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.359668+0800 wftest[4073:69627] task2 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
2019-03-30 14:01:51.360195+0800 wftest[4073:69627] task3 -- <NSThread: 0x600001bdcd00>{number = 1, name = main}
可以看到,虽然是并发队列,但因为是同步任务,所以也是按顺序执行的。因为是同步任务,所以就在当前线程,主线程中执行。异步任务则会在子线程中执行。
(可以简单总结:串行 ,要等待上个任务执行完毕,才执行下个任务,所以会在同一个线程中执行。 并行:不用等上个任务执行完毕,就可以执行下个任务。同步:在当前线程中执行,不会开辟子线程。异步:在子线程中执行(这是指串行和并行队列。后面说的主队列异步,也是在主线程中执行))。
再来看并发队列的异步执行任务:
//并发队列的异步执行
-(void)concurrentAsyn{
//1.创建队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.feng", DISPATCH_QUEUE_CONCURRENT);
//2.创建任务
void(^task1) (void) = ^(){
NSLog(@"task1 -- %@",[NSThread currentThread]);
};
void(^task2) (void) = ^(){
NSLog(@"task2 -- %@",[NSThread currentThread]);
};
void(^task3) (void) = ^(){
NSLog(@"task3 -- %@",[NSThread currentThread]);
};
//3.把任务添加到队列中去
dispatch_async(concurrentQueue, task1);
dispatch_async(concurrentQueue, task2);
dispatch_async(concurrentQueue, task3);
}
来看打印情况:
2019-03-30 14:02:45.003384+0800 wftest[4112:70392] 主线程 -- <NSThread: 0x600000ba8c40>{number = 1, name = main}
2019-03-30 14:02:45.005834+0800 wftest[4112:70450] task2 -- <NSThread: 0x600000bf7840>{number = 4, name = (null)}
2019-03-30 14:02:45.005834+0800 wftest[4112:70449] task1 -- <NSThread: 0x600000bcc380>{number = 3, name = (null)}
2019-03-30 14:02:45.005838+0800 wftest[4112:70454] task3 -- <NSThread: 0x600000bcc480>{number = 5, name = (null)}
可以看到,异步任务另外开辟了子线程。可以看到打印顺序发生了变化。
接着,我们来看全局队列。
全局队列的工作表现和并发队列一致。
但是全局队列是否就是并发队列呢?不是的。我们来看下他们的区别:
1.全局队列没有名称,无论是MRC&ARC都不用考虑释放,所以在日常开发中,建议使用全局队列。
2.并发队列:有名字,和NSThread的name属性作用类似,如果你在MRC的开发中,则需要使用dispatch_releas(q)来释放对应的对象。
那么并发队列在什么时候使用呢?在你开发第三方的框架的时候,则需要使用并发队列了。这样可以避开和使用你的开发框架的程序员弄混队列。
咱们先来看下全局队列的同步任务。(日常开发中几乎用不到。)
//全局队列的同步任务
-(void)globalSync{
NSLog(@"begin");
//1.创建全局队列
dispatch_queue_t gloabalQueue = dispatch_get_global_queue(0, 0);
//2.创建任务
void(^task1) (void) = ^(){
NSLog(@"task1 -- %@",[NSThread currentThread]);
};
void(^task2) (void) = ^(){
NSLog(@"task2 -- %@",[NSThread currentThread]);
};
void(^task3) (void) = ^(){
NSLog(@"task3 -- %@",[NSThread currentThread]);
};
//3.加入任务到队列中执行
dispatch_sync(gloabalQueue, task1);
dispatch_sync(gloabalQueue, task2);
dispatch_sync(gloabalQueue, task3);
NSLog(@"end");
}
来看打印结果:
2019-03-30 14:35:08.160967+0800 wftest[5189:88230] 主线程 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161223+0800 wftest[5189:88230] begin
2019-03-30 14:35:08.161470+0800 wftest[5189:88230] task1 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161639+0800 wftest[5189:88230] task2 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161773+0800 wftest[5189:88230] task3 -- <NSThread: 0x600000684300>{number = 1, name = main}
2019-03-30 14:35:08.161891+0800 wftest[5189:88230] end
可以看到,按熟悉执行,同步的,都是当前线程中执行的。
再来看全局队列的异步任务,他是在子线程池上执行的,每个任务都有一个自己的线程,前提是线程池里有线程资源,底层有一个线程重用机制的。看下代码:
2019-03-30 14:39:06.429812+0800 wftest[5330:90708] 主线程 -- <NSThread: 0x600003606580>{number = 1, name = main}
2019-03-30 14:39:06.430060+0800 wftest[5330:90708] begin
2019-03-30 14:39:06.430228+0800 wftest[5330:90708] end
2019-03-30 14:39:06.430381+0800 wftest[5330:90762] task1 -- <NSThread: 0x600003660a40>{number = 3, name = (null)}
2019-03-30 14:39:06.430416+0800 wftest[5330:90763] task3 -- <NSThread: 0x600003660a00>{number = 4, name = (null)}
2019-03-30 14:39:06.430422+0800 wftest[5330:90760] task2 -- <NSThread: 0x600003660ec0>{number = 5, name = (null)}
可以看到,每个任务都有自己的独立的线程。
有点累,一会再来看看主队列。
主队列的特点:
1.专门用来在主线程上调度任务的队列。
2.不会开启子线程
3.以先进先出的方式,在主线程空闲的时候才会调度主队列中的任务在主线程中执行。
4.如果当前主线程中有任务在执行,那么无论主队列中添加了什么任务,都不会被调度。
主队列是负责在主线程中调度任务的。
会随着程序启动一起创建。
对于我们程序员来说,主队列只需要获取,不需要创建。
那么我们来看下主队列的异步任务的代码:
//主队列的异步任务
-(void)mainAsync{
NSLog(@"begin"); //1.创建主队列
dispatch_queue_main_t mainAsync = dispatch_get_main_queue(); //2.创建任务
void(^task1) (void) = ^(){
NSLog(@"task1 -- %@",[NSThread currentThread]);
};
void(^task2) (void) = ^(){
NSLog(@"task2 -- %@",[NSThread currentThread]);
};
void(^task3) (void) = ^(){
NSLog(@"task3 -- %@",[NSThread currentThread]);
};
//添加任务到队列
dispatch_async(mainAsync, task1);
dispatch_async(mainAsync, task2);
dispatch_async(mainAsync, task3);
NSLog(@"end");
}
来看看打印情况:
2019-04-01 16:23:52.002922+0800 wftest[2320:37104] 主线程 -- <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.003178+0800 wftest[2320:37104] begin
2019-04-01 16:23:52.003342+0800 wftest[2320:37104] end
2019-04-01 16:23:52.092118+0800 wftest[2320:37104] task1 -- <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.092324+0800 wftest[2320:37104] task2 -- <NSThread: 0x600001676a80>{number = 1, name = main}
2019-04-01 16:23:52.092497+0800 wftest[2320:37104] task3 -- <NSThread: 0x600001676a80>{number = 1, name = main}
大家可以注意到这里的几个情况:
1.虽然是异步的,但是三个任务仍然按顺序调度执行。
2.先执行了begin,紧接着执行的了end.然后才执行了三个任务,也就是说,主线程有空闲的时候才执行这三个任务。
我们说下deadlock死锁:是两个或者更多的线程之间出现的情况:比如第一个线程在等待第二个线程的完成才能继续执行,而第二个线程在等待第一个线程的完成才能继续执行。
看起来似乎异步更有用,效率更高,那么同步有什么用呢,我们说下同步的作用。
1.首先,同步肯定是保证了任务执行的顺序。
2.可以让后面的异步任务要依赖于某一个同步的任务。比如,必须让用户登录之后,才允许他下载电影。
我们看下代码:
//同步+异步
-(void)loadMovies{
dispatch_async(dispatch_get_global_queue(0, 0), ^{//开辟一条子线程
NSLog(@"开辟了子线程----%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
//登录,在当前的线程执行
NSLog(@"登录了---%@", [NSThread currentThread]);
sleep(3);
}); //2.同时下载3部电影
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"正在下载第一部电影---%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"正在下载第二部电影---%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"正在下载第三部电影---%@",[NSThread currentThread]);
}); dispatch_sync(dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"计算机将在三秒后关闭 --%@",[NSThread currentThread]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"关机了---%@", [NSThread currentThread]);
});
});
});
}
然后我们来看打印log:
2019-04-01 17:52:46.491858+0800 wftest[5359:81060] 开辟了子线程----<NSThread: 0x600002ff8580>{number = 3, name = (null)}
2019-04-01 17:52:46.492263+0800 wftest[5359:81060] 登录了---<NSThread: 0x600002ff8580>{number = 3, name = (null)}
2019-04-01 17:52:49.497610+0800 wftest[5359:81063] 正在下载第一部电影---<NSThread: 0x600002ffaa00>{number = 4, name = (null)}
2019-04-01 17:52:49.497634+0800 wftest[5359:81062] 正在下载第三部电影---<NSThread: 0x600002ffefc0>{number = 6, name = (null)}
2019-04-01 17:52:49.497654+0800 wftest[5359:81061] 正在下载第二部电影---<NSThread: 0x600002ffef40>{number = 5, name = (null)}
2019-04-01 17:52:50.498825+0800 wftest[5359:80998] 计算机将在三秒后关闭 --<NSThread: 0x600002f9d600>{number = 1, name = main}
2019-04-01 17:52:53.752223+0800 wftest[5359:80998] 关机了---<NSThread: 0x600002f9d600>{number = 1, name = main}
我们注意到这几个方面:虽然下载电影的时候,又开启了三个新的线程,但是他们仍然要等待登录后,才能执行,以及最后,计算机回到主队列去关闭计算机的时候,也是等电影下载完毕。这是因为主队列这里的也是同步任务。前面也是同步任务。
接下来我们来看下dispatch_time的延迟操作
什么时候使用dispatch_after呢?
1.最好坚持在主队列上使用dispatch_after。而不是在自定义串行队列上,并发队列也尽量不要使用。
2.主队列(串行)是使用dispatch_after的最好选择。xcode也提供了自动完成模板。
我们来看下代码:
//延迟执行
-(void)delay{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
void(^task)(void)=^(){
NSLog(@"%@",[NSThread currentThread]);
};
//主队列
dispatch_after(when, dispatch_get_main_queue(), task);
NSLog(@"come here");
}
来看打印:
2019-04-02 10:55:47.020688+0800 wftest[2069:28686] come here
2019-04-02 10:55:49.216037+0800 wftest[2069:28686] <NSThread: 0x6000018e1c40>{number = 1, name = main}
IOS提供的一些方便使用的延迟:
//延迟执行
-(void)after{
[self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0];
}
再来看看线程安全:
线程安全是多线程不可避免的问题。
dispatch_once以线程安全的方式执行,仅且执行代码一次。她会给代码设立一个临界区。试图访问临界区(即要传递给dispatch_onece的代码)的不同线程,在临界区已经有一个线程在执行的情况下会被阻塞,直到临界区完成为止。
我们来看下使用dispatch_once来实现单例线程安全:
//使用dispatch_once实现线程安全的单例
+(instancetype)sharedSingleton{
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
如果一个单例中的单例属性是一个可变对象。那么就要考虑线程安全问题了。比如NSMutableArray。可能会出现一个线程正在读取,另外一个线程正在修改。这样就会出现线程不安全的情况。在GCD中可以通过dispatch_barrier_async来进行创建读写锁,这样一个解决方案。
接下来,我们来看下调度组(dispatch_group):
调度组的实现原理:类似引用计数,进行+1,-1;
应用场景:
比如当你开启了下载任务,当下载三个任务,只有等这三个任务全部下载完毕后,才能下一步做事情。这个时候就可以用到调度组,这个调度组,就能监听它里面的任务是否执行完毕:
//调度组
-(void)groupDispatch{
//1.创建调度组
dispatch_group_t group = dispatch_group_create();
//2.获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//3.创建三个下载任务
void(^task1)(void) = ^(){
NSLog(@"%@----下载片头",[NSThread currentThread]);
}; dispatch_group_enter(group);//引用计算+1
void (^task2) (void) = ^(){
NSLog(@"%@---下载内容",[NSThread currentThread]);
[NSThread sleepForTimeInterval:3.0];
NSLog(@"----下载内容完毕");
dispatch_group_leave(group);//引用计数-1
}; dispatch_group_enter(group);//引用计数+1
void(^task3)(void)=^(){
NSLog(@"%@----下载片尾",[NSThread currentThread]);
dispatch_group_leave(group);//引用计数-1
}; //4.需要将我们的队列和任务放到组内去监控
dispatch_group_async(group, queue, task1);
dispatch_group_async(group, queue, task2);
dispatch_group_async(group, queue, task3); //5.监听函数
// 参数2,表示参数3这里的代码在哪个队列中执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//表示组内所有的任务都完成之后,执行这里的代码
NSLog(@"把下载好的视频按顺序拼接好,然后显示在UI上播放%@",[NSThread currentThread]);
}); }
看打印:
2019-04-02 16:18:45.168288+0800 wftest[12368:305146] <NSThread: 0x6000023d5fc0>{number = 4, name = (null)}---下载内容
2019-04-02 16:18:45.168288+0800 wftest[12368:305147] <NSThread: 0x6000023d5f00>{number = 3, name = (null)}----下载片头
2019-04-02 16:18:45.168288+0800 wftest[12368:305149] <NSThread: 0x6000023e9c80>{number = 5, name = (null)}----下载片尾
2019-04-02 16:18:48.174292+0800 wftest[12368:305146] ----下载内容完毕
2019-04-02 16:18:48.174604+0800 wftest[12368:305085] 把下载好的视频按顺序拼接好,然后显示在UI上播放<NSThread: 0x6000023b25c0>{number = 1, name = main}
dispatch_group_enter手动通知group任务已经开始。注意,enter和leave必须成对。否则会造成诡异崩溃问题。
最后,再来看定时源事件和子线程的运行循环:
-(void)myMain{
//1.定义一个定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvnet) userInfo:nil repeats:YES];
//2.将定时器加入到运行循环中,只有当加入到运行循环中,他才知道这个时候,有一个定时任务
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
} -(void)timeEvnet{
NSLog(@"%d----%@",self.count,[NSThread currentThread]);
if(self.count++ == 10){
NSLog(@"挂了");
//停止当前的运行循环
CFRunLoopStop(CFRunLoopGetCurrent());
}
}
ios高级开发之多线程(三)GCD技术的更多相关文章
- ios高级开发之多线程(二)NSThread技术
多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...
- IOS高级开发之多线程(四)NSOperation
1.什么是NSOperation,NSOperationQueue? NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作. ...
- ios高级开发之多线程(一)
1.概念: 多线程(multithreading)到底是什么呢,它是指在软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件的支持,而能够在同一时间执行多个线程,进而提升整体处理性 ...
- iOS 高级开发 runtime(三)
三 .动态添加方法 我们可以通过runtime动态地添加方法.那么到底啥叫动态添加方法呢?动态添加方法就是当我们程序运行时才知道我们应该调用哪个方法.我们首先需要了解这一点,当我们编写完一段代码后,我 ...
- IOS高级开发之多线程(五)NSOperation 2
接着看NSOperation.NSOperationQueue线程间的通信: 应用场景:比如我们经常把一些耗时的操作比如下载图片放在子线程,那么当这个完成之后,我们就需要回到主线程,这个时候就需要用到 ...
- 移动开发在路上-- IOS移动开发系列 多线程三
这一次说一点概念性的东西,也是为后边做一些基础 HTTP协议的基本概念 http协议的基本概念 全称“超文本传输协议”,浏览器和服务器之间的通信规则 HTTp协议永远都是客户端发起的请求,服务器回送响 ...
- (转发)IOS高级开发~Runtime(三)
11.系统类的方法实现部分替换 - (void) methodExchange { Method m1 = class_getInstanceMethod([NSStringclass],@selec ...
- (转发)IOS高级开发~Runtime(四)
用C代替OC: #import <objc/runtime.h> #import <objc/message.h> #import <stdio.h> extern ...
- (转发)IOS高级开发~Runtime(二)
一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...
随机推荐
- Cocos2dx Android环境编译出错:jni/Android.mk: Cannot find module with tag 'scripting/lua-bindings' in import path
解决方案为: 在项目proj.android\jni\Android.mk(D:\my_lua_test2\MyluaTest\frameworks\runtime-src\proj.android\ ...
- ArrayList 和 LinkList 的区别
ArrayList 的相关知识 public class ArrayList<E> extends AbstractList<E>implements List<E> ...
- talend工具中往oracle插数据报ORA-01461: can bind a LONG value only for insert into a LONG colum
今天使用talend往oracle插数据报ORA-01461: can bind a LONG value only for insert into a LONG column 数据源是mysql,开 ...
- Linux资源分析工具杂谈(长文慎入)
Linux资源分析工具杂谈 开篇之前请大家先思考一个问题: 磁盘的平均I/O响应时间是1 ms,这个指标是好,还是差? 众所周知,计算机科学是客观的,也就是说对于一个给定的问题,我们总是 ...
- Python练习:爬取图片
贴吧地址 https://tieba.baidu.com/p/5272413637?red_tag=0606091703 程序如下import urllib.requestimport re def ...
- php $_SERVER中的SERVER_NAME 和HTTP_HOST的区别以及REQUEST_URI的讲解
今天再次遇到了这个问题通过$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']获取域名及请求的URL的问题,便再次百度了,发现没学习一次都有新的知识,便分享出 ...
- CentOS7中OpenVPN的配置
最近需要在openstack中集成openvpn功能,故熟悉了一下openvpn的搭建流程,记录下来,供参考 版本:openvpn-2.3.4.tar.gz 下载地址:http://pan.baidu ...
- ajax请求网络api
不啰嗦,直接上代码: 1.在浏览器输入网址:http://api.asilu.com/weather/?callback=getname&city=深圳 你会看到如下结果:他返回的是json数 ...
- rpm包与 yum 安装与卸载
rpm包的安装: 1.安装一个包 # rpm -ivh 2.升级一个包 # rpm -Uvh 3.移走一个包 # rpm -e 4.安装参数 --force 即使覆盖属于其它包的文件也强迫安 ...
- Matlab学以致用--模拟os任务装载情况
2012-06-08 21:26:42 用matlab来建模,仿真不同时刻os task在队列中的装载情况.输入参数如下 作为初学者,M文件写的有点长.能实现功能就算学以致用了. clear;clc ...