一、initialize 探索
+ (void)initialize
{
NSLog(@"%@:%s",self, __func__);
}
+(void)load{
NSLog(@"load");
}
打印结果
2019-09-23 19:57:29.340990+0800 LGDemo1[29860:793430] load
+ (void)initialize
{
NSLog(@"%@:%s",self, __func__);
}
+(void)load{
NSLog(@"%@:%s",self, __func__);
}
打印结果
2019-09-23 19:53:41.120539+0800 LGDemo1[29639:783019] LGPerson:+[LGPerson initialize]
2019-09-23 19:53:41.121399+0800 LGDemo1[29639:783019] LGPerson:+[LGPerson load]
结论:
- 1:
initialize调用时机是在接收到第一条消息的时候调用。 - 2:当
NSLog(@"%@:%s",self, __func__);的self调用的时候即触发了方法调用栈。_objc_msgSend_uncached通过消息查找机制调用。 - 3:调用顺序,父类-->子类 supercls = cls ->superclass
- 4:调用次数,多次。
- 5:分类中实现,只执行分类的调用。
- 6:作用:初始化常量。
二、 NSOperation探索
1:串行VS并行
串行和并行描述的是任务和任务之间的执行方式。串行是任务A执行完了任务B才执行。并行则是任务A和任务B可以同时执行。
2:同步VS异步
同步和异步描述的是函数什么时候返回。
3:NSOperation指代的是操作(任务),使用必须子类话(NSInvovationOperation和NSBlockOperation)
4:可以自定义使用,独立使用或者使用NSOperationQueue配合读写使用。
例1:独立使用
使用NSBlockOperation执行当前任务
NSBlockOperation *op = [[NSBlockOperation alloc] init];
[op addExecutionBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"lg_blockOpTestA --------------- %@ ---------------", [NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"lg_blockOpTestB --------------- %@ ---------------", [NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"lg_blockOpTestC --------------- %@ ---------------", [NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"lg_blockOpTestD --------------- %@ ---------------", [NSThread currentThread]);
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"lg_blockOpTestE --------------- %@ ---------------", [NSThread currentThread]);
}
}];
[op start];
NSLog(@"========end=========");
打印结果
019-09-30 10:15:41.052295+0800 LGOPTest[8076:3216426] lg_blockOpTestE --------------- <NSThread: 0x60000296cf80>{number = 6, name = (null)} ---------------
2019-09-30 10:15:41.052295+0800 LGOPTest[8076:3216422] lg_blockOpTestB --------------- <NSThread: 0x60000295a400>{number = 4, name = (null)} ---------------
2019-09-30 10:15:41.052295+0800 LGOPTest[8076:3216266] lg_blockOpTestA --------------- <NSThread: 0x600002900280>{number = 1, name = main} ---------------
2019-09-30 10:15:41.052299+0800 LGOPTest[8076:3216423] lg_blockOpTestD --------------- <NSThread: 0x600002961840>{number = 3, name = (null)} ---------------
2019-09-30 10:15:41.052301+0800 LGOPTest[8076:3216420] lg_blockOpTestC --------------- <NSThread: 0x60000294e040>{number = 5, name = (null)} ---------------
2019-09-30 10:15:41.052455+0800 LGOPTest[8076:3216422] lg_blockOpTestB --------------- <NSThread: 0x60000295a400>{number = 4, name = (null)} ---------------
2019-09-30 10:15:41.052457+0800 LGOPTest[8076:3216426] lg_blockOpTestE --------------- <NSThread: 0x60000296cf80>{number = 6, name = (null)} ---------------
2019-09-30 10:15:41.052473+0800 LGOPTest[8076:3216423] lg_blockOpTestD --------------- <NSThread: 0x600002961840>{number = 3, name = (null)} ---------------
2019-09-30 10:15:41.052490+0800 LGOPTest[8076:3216266] lg_blockOpTestA --------------- <NSThread: 0x600002900280>{number = 1, name = main} ---------------
2019-09-30 10:15:41.052499+0800 LGOPTest[8076:3216420] lg_blockOpTestC --------------- <NSThread: 0x60000294e040>{number = 5, name = (null)} ---------------
2019-09-30 10:15:41.052546+0800 LGOPTest[8076:3216422] lg_blockOpTestB --------------- <NSThread: 0x60000295a400>{number = 4, name = (null)} ---------------
2019-09-30 10:15:41.052563+0800 LGOPTest[8076:3216426] lg_blockOpTestE --------------- <NSThread: 0x60000296cf80>{number = 6, name = (null)} ---------------
2019-09-30 10:15:41.053343+0800 LGOPTest[8076:3216420] lg_blockOpTestC --------------- <NSThread: 0x60000294e040>{number = 5, name = (null)} ---------------
2019-09-30 10:15:41.052814+0800 LGOPTest[8076:3216423] lg_blockOpTestD --------------- <NSThread: 0x600002961840>{number = 3, name = (null)} ---------------
2019-09-30 10:15:41.052999+0800 LGOPTest[8076:3216266] lg_blockOpTestA --------------- <NSThread: 0x600002900280>{number = 1, name = main} ---------------
2019-09-30 10:15:41.113780+0800 LGOPTest[8076:3216266] ========end=========
可以看到并不是多有线程都在main主线程。NSBlockOperation会阻塞当前的线程。
NSInvovationOperation都是在主线程执行的。NSInvocationOperation比较简单,就是继承了NSOperation,区别就是它是基于一个对象和selector来创建操作,可以直接使用而不需继承来实现自己的操作处理。当修改已有程序中已经有处理操作的selector可以考虑使用NSInvocationOperation。
2)自定义步骤
- 1
main同步 - 2
start异步 - 3 重写方法 1) 修改当前状态 2) 创建下载dataTask 3) 错误处理 4:根据
finished判断当前线程是否完成。 - 4 完成之后1:清除其他的依赖 2:将当前的任务在队列中移除 3:执行
completedBlock - 5
asynchronous是否具备异步的能力 - 6
executing在重写的时候需要关注的字段
三、NSOperation的start方法。
找到libs-base-master,来到NSOperation的start方法。
1:NSAutoreleasePool包裹的代码块执行的。
2:executing
if (NO == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
internal->executing = YES;
[self didChangeValueForKey: @"isExecuting"];
}//手动的kvo通知
如果当前任务没有取消
NS_DURING
{
if (NO == [self isCancelled])
{
[NSThread setThreadPriority: internal->threadPriority];
[self main];
}
}
会执行main函数进行操作。 默认main还是是return。什么都没做。
- (void) main;
{
return; // OSX default implementation does nothing
}
当main函数执行完了之后用调用[self _finish]进行标示的重置。 结论:main函数不具备异步的能力。是因为当main执行完了就会调用[self _finish]结束了。所以在main函数中开辟一个子线程执行任务是无意义的。
3:_finish方法 也是在修改状态值。
- (void) _finish
{
/* retain while finishing so that we don't get deallocated when our
* queue removes and releases us.
*/
[self retain];
[internal->lock lock];
if (NO == internal->finished)
{
if (YES == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
[self willChangeValueForKey: @"isFinished"];
internal->executing = NO;
internal->finished = YES;
[self didChangeValueForKey: @"isFinished"];
[self didChangeValueForKey: @"isExecuting"];
}
else
{
[self willChangeValueForKey: @"isFinished"];
internal->finished = YES;
[self didChangeValueForKey: @"isFinished"];
}
if (NULL != internal->completionBlock)
{
CALL_BLOCK_NO_ARGS(internal->completionBlock);//额外的属性,可以进行调用
}
}
[internal->lock unlock];
[self release];
}
以上只设计状态的改变不涉及线程的操作。 例2:(面试题)有A、B、C、D、E、F六个任务,A和B执行完了D才能执行,B和C执行完了才能执行E,D和E执行完了才能执行F。 解答:可以通过NSOperation的依赖关系来处理这样的调度关系。还可以通过GCD和group barrier实现?
//A B C D E F
//A,B - D
//B,C - E
//D,E - F
- (void)lg_op_test{
//A
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"A --------------- %@", [NSThread currentThread]);
// }
}];
//B
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
// sleep(3);
for (int i = 0; i < 3; i ++) {
NSLog(@"B --------------- %@", [NSThread currentThread]);
}
}];
//C
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"C --------------- %@", [NSThread currentThread]);
}
}];
//D
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"D --------------- %@", [NSThread currentThread]);
}
}];
//E
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"E --------------- %@", [NSThread currentThread]);
}
}];
//F
NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i ++) {
NSLog(@"F --------------- %@", [NSThread currentThread]);
}
}];
//op1 ,op2, op3 :isReady 添加依赖关系
[op4 addDependency: op1];
[op4 addDependency: op2];
[op5 addDependency: op2];
[op5 addDependency: op3];
[op6 addDependency:op4];
[op6 addDependency:op5];
//添加队列当中(串行、并发)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//并发与否取决于这个值
queue.maxConcurrentOperationCount = 6;
[queue addOperation: op6];
[queue addOperation: op5];
[queue addOperation: op4];
[queue addOperation: op3];
[queue addOperation: op2];
[queue addOperation: op1];
}
依赖的关系决定当前任务的执行方式。 4:addDependency都做了什么?
存在AB两个任务,A任务依赖B任务执行
- (void) addDependency: (NSOperation *)op
{
*****前面做一些条件判断******
[internal->lock lock];
if (internal->dependencies == nil)
{
internal->dependencies = [[NSMutableArray alloc] initWithCapacity: 5];//声明一个数组
}
NS_DURING
{
if (NSNotFound == [internal->dependencies indexOfObjectIdenticalTo: op])
{
[self willChangeValueForKey: @"dependencies"];
[internal->dependencies addObject: op];//存放B任务
/* We only need to watch for changes if it's possible for them to
* happen and make a difference.
*/
if (NO == [op isFinished]
&& NO == [self isCancelled]
&& NO == [self isExecuting]
&& NO == [self isFinished])
{
/* Can change readiness if we are neither cancelled nor
* executing nor finished. So we need to observe for the
* finish of the dependency.
*/
//A任务监听B任务的状态,监听isFinished字段
[op addObserver: self
forKeyPath: @"isFinished"
options: NSKeyValueObservingOptionNew
context: isFinishedCtxt];
if (internal->ready == YES)
{
/* The new dependency stops us being ready ...
* change state.
*/
[self willChangeValueForKey: @"isReady"];
internal->ready = NO;
[self didChangeValueForKey: @"isReady"];
}
}
[self didChangeValueForKey: @"dependencies"];
}
}
NS_HANDLER
{
[internal->lock unlock];
NSLog(@"Problem adding dependency: %@", localException);
return;
}
NS_ENDHANDLER
[internal->lock unlock];
}
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
[internal->lock lock];
/* We only observe isFinished changes, and we can remove self as an
* observer once we know the operation has finished since it can never
* become unfinished.
*/
[object removeObserver: self forKeyPath: @"isFinished"];
if (object == self)
{
/* We have finished and need to unlock the condition lock so that
* any waiting thread can continue.
*/
[internal->cond lock];
[internal->cond unlockWithCondition: 1];
[internal->lock unlock];
return;
}
if (NO == internal->ready)
{
NSEnumerator *en;
NSOperation *op;
/* Some dependency has finished (or been removed) ...
* so we need to check to see if we are now ready unless we know we are.
* This is protected by locks so that an update due to an observed
* change in one thread won't interrupt anything in another thread.
*/
en = [internal->dependencies objectEnumerator];
while ((op = [en nextObject]) != nil)
{
if (NO == [op isFinished])
break;
}
if (op == nil)
{
[self willChangeValueForKey: @"isReady"];
internal->ready = YES;
[self didChangeValueForKey: @"isReady"];
}
}
[internal->lock unlock];
}
总结:通过addDependency设置依赖关系的原理是:假设有AB两个任务,B任务存储在一个数组里,A任务监听B任务的isFinished字段。这时A任务的isReady字段被置为NO,当遍历到B任务isFinished结束的时候将A任务的isReady置为YES。就可以开始调度当前的A任务了。