SDWebImage(3)

240 阅读8分钟

一、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指代的是操作(任务),使用必须子类话(NSInvovationOperationNSBlockOperation

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 在重写的时候需要关注的字段

三、NSOperationstart方法。

找到libs-base-master,来到NSOperationstart方法。 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任务了。