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技术的更多相关文章

  1. ios高级开发之多线程(二)NSThread技术

    多线程技术是有多套解决方案的,那么我们该如何选择呢? 技术方案 简介 语言 线程生命周期 使用频率 pthread 1.一套通用的多线程API 2.适用于UNIX,linux,windows等 3.跨 ...

  2. IOS高级开发之多线程(四)NSOperation

    1.什么是NSOperation,NSOperationQueue? NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作. ...

  3. ios高级开发之多线程(一)

    1.概念: 多线程(multithreading)到底是什么呢,它是指在软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件的支持,而能够在同一时间执行多个线程,进而提升整体处理性 ...

  4. iOS 高级开发 runtime(三)

    三 .动态添加方法 我们可以通过runtime动态地添加方法.那么到底啥叫动态添加方法呢?动态添加方法就是当我们程序运行时才知道我们应该调用哪个方法.我们首先需要了解这一点,当我们编写完一段代码后,我 ...

  5. IOS高级开发之多线程(五)NSOperation 2

    接着看NSOperation.NSOperationQueue线程间的通信: 应用场景:比如我们经常把一些耗时的操作比如下载图片放在子线程,那么当这个完成之后,我们就需要回到主线程,这个时候就需要用到 ...

  6. 移动开发在路上-- IOS移动开发系列 多线程三

    这一次说一点概念性的东西,也是为后边做一些基础 HTTP协议的基本概念 http协议的基本概念 全称“超文本传输协议”,浏览器和服务器之间的通信规则 HTTp协议永远都是客户端发起的请求,服务器回送响 ...

  7. (转发)IOS高级开发~Runtime(三)

    11.系统类的方法实现部分替换 - (void) methodExchange { Method m1 = class_getInstanceMethod([NSStringclass],@selec ...

  8. (转发)IOS高级开发~Runtime(四)

    用C代替OC: #import <objc/runtime.h> #import <objc/message.h> #import <stdio.h> extern ...

  9. (转发)IOS高级开发~Runtime(二)

    一些公用类: @interface ClassCustomClass :NSObject{ NSString *varTest1; NSString *varTest2; NSString *varT ...

随机推荐

  1. 11、jeecg 笔记之 界面常用整理 - 方便复制粘贴

    1.datagrid 操作按钮(按钮样式) 操作按钮的显示主要依赖于 <t:dgCol title="操作" field="opt"  ></ ...

  2. Oracle数据库查询所有关键字

    管理员账户登录后,执行以下命令:  select * from v$reserved_words

  3. Redis和mysql数据怎么保持数据一致的?

    需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库.   这个业务场景, ...

  4. cc.Mask. 纯代码拉伸遮罩

    var imgBoxInner = new cc.Node('imgBoxInner');var mask = imgBoxInner.addComponent(cc.Mask);mask.alpha ...

  5. 3#Java案例

    以下内容引用Github地址https://github.com/DuGuQiuBai/Java/blob/master/day01/code/02_%E5%B8%A6%E6%B3%A8%E9%87% ...

  6. Setting NLS_LANG Value for Oracle

    Introduction Many times, when you have an Oracle application and you have to support special charact ...

  7. 新增职责 不能从IE进入的问题 此责任无可用函数 (转)

    此责任无可用函数(The Function Is Not Available Under The Responsibility) When attempting to navigate to a fu ...

  8. java核心技术第十版 笔记

    1.java区分大小写 2.类名是以大写字母开头 (驼峰) 3.http://docs.oracle.com/javase/specs  java语言规范 4. /* */ 注释不能嵌套 5. Jav ...

  9. golang 基本数据结构使用

    1 goalng struct 1.1 var s1 student 1.2 s2 := student {"zhou", 33} 1.3 s3 := student {Name: ...

  10. centos7安装nginx1.10.1

    安装nginx. 1.首先在根目录下创建一个software文件夹用来存储下载的压缩包. 2.然后cd跳转的software文件夹下,进行压缩包的下载 wget -c https://nginx.or ...