review

Block

Block三种类型

  • NSGlobalBlock,全局block存放在静态区,block没有使用外部变量情况。
  • NSStackBlock,在调用的了外部变量或并且未被copy,使用完后自动销毁
  • NSMallocBlock,对stack block进行copy后变成堆,需要手动释放
 	void(^globalBlock)(void) = ^(){
        NSLog(@"run globalBlock");
    };
    globalBlock(); 
	
	int a = 10;
    int(^stackBlock)(int b)=^(int v){
        //进行值传递
        NSLog(@"statckBlock %d",a);
        return 0;
    };
    stackBlock(1);`
**注意**:但是在ARC环境下,系统会自动将栈区的block,copy到堆区变成mallocBlock

Block中的循环引用问题

  • 对于系统自带的block或者masonry中的涉及到的block为什么不会造成循环引用,造成循环引用的原因是各自强引用的对方导致的,而系统的block使用的是栈block,立即执行立即出栈不会造成持有的情况,只有mallocBlock堆区的内容需要进行手动管理因为进行了copy操作

Block的对象提前释放问题处理

当pop后self会释放,当定时器执行时向nil发送消息最后打印出nil

 __weak typeof(self) weakSelf = self;
    blockTest block = ^(){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"weak name is %@",weakSelf.name);
        });
    };
  block();

对于这种情况解决方案:
1.增加判断,增加判断如果为空不去执行操作
2.如果这段代码是业务必须的那么使用__strong关键字进行修饰强引用.

//    __strong typeof(self) strongSelf = self;
    __strong NSString *name = self.name;
    blockTest blockTest1 = ^(){
//        strongSelf.name = @"change name";
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",name);
        });
    };
    //使用invoke调用函数
    [blockTest1 invoke];

Block的几种调用方式

  1. 直接调用
  2. 通过函数的参数进行调用(匿名函数立即执行)
  3. 通过invoke函数调用(无参数的时候可以方便)
  4. 通过NSInvocation发送消息进行调用(有参时可以采用函数签名调用,挺麻烦的)

NSInvocation与perforselector的区别前者可以设置多个参数去调用函数,在添加参数方法中的Index必须是从2开始的,如果是数字参数要传入‘&’值的地址.

	NSMethodSignature *signature = [[self class]  instanceMethodSignatureForSelector:@selector(runBlock:)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setTarget:self];
    invocation.selector = @selector(runBlock:);
    //参数必须从第二个索引开始
    int index = 1;
    [invocation setArgument:&index atIndex:2];
    [invocation invoke];

查看Objective-C的源码转换方法

clang -rewrite-objc main.m
控制台执行命令会产生一个.cpp的源码文件.

多线程

线程的定义

程序执行的最小单元,一个进程可以有多个线程

  • 多线程

理论上作为单核的CPU只能每次执行一个线程,但是可以通过高速的调用线程进行切换从而达到多线程并发执行的目的.

  • 优点

可以尽可能的发挥CPU的性能,提高CPU的使用率

  • 缺点

如果线程过多会增加CPU的负担,增加系统的开销,影响系统的性能.

任务

  • 同步任务:会阻塞当前的线程执行.
  • 异步任务:无需等待可以同时执行多个任务.具有开启线程的能力,如果不是添加到主队列上,异步会在子线程中执行任务.

队列

是指在执行任务等待的队列,先进先出FIFO原则,每次都是从队列的头部进行读取.

  • 串行队列 (Serial Dispatch Queue)

同步队列,同一时间队列中只能执行一个任务,单线程执行任务

  • 并发队列 (Concurrent Dispatch Queue)

同一时间队列中可以执行多个任务,前提是当前的队列是在一个异步的(Asyc)异步任务中.多线程同时执行任务

线程的创建方式

  1. NSThread

启动方式有手动启动和构造器的方式进行启动,只要是手动启动的话都需要对线程的生命周期进行管理包括最后的释放

  1. GCD(Grand Central Dispatch)
    优点:

    • 会自动管理线程的生命周期
    • 苹果公司为了更好的利用CPU资源提出的解决方案能够给更好的计算并发任务
  2. NSOperationQueue

  • 实际项目开发中,如果没有复杂的异步线程关系管理,一般使用GCD去做不需要管理线程的生命周期.
  • 如果考虑异步操作之间的事务性,顺序行,依赖关系,做得话系统已经内置了很多的方法去实现.
  • 因为使用它创建线程需要我么自己去管理线程的生命的周期,需要考虑同步,异步,加锁等问题,在性能上增加开销.

经典窗口卖票资源抢占问题

  • NSThread
for(int i=0; i<300; i++){
	//当循环次数较多的时候加上autoreleasepool可以提交程序的性能,可以有效的及时对循环内所持有的对象及时释放
	//NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
	//thread.name = [NSString stringWithFormat:@"sale %@",@(i)];
	//[thread start];
	//[thread autorelease];
	//[pool drain];
	@autoreleasepool {
		NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
		thread.name = [NSString stringWithFormat:@"sale %@",@(i)];
		[thread start];
	}
}

- (void)saleTicket{
    while (true) {
        [NSThread sleepForTimeInterval:1];
        //关键字加锁防止多线程访问共享资源引起数据错乱
        @synchronized (self) {
            if(self.num == 0){
                break;
            }else{
                self.num--;
                NSLog(@"%@,%d",[NSThread currentThread].name,self.num);
            }
        }
    }
}
  • GCD
    dispatch_barrier_async:异步不会阻塞线程,使用场景,加入前面线程结果影响后面的线程则可以使用它进行分割,只有前面的线程执行完毕才会继续跑后面的,条件是同一个队列下才有效.

dispatch_io_t可以用于快速读取大文件里的内容,可以进行文件分割切片使用异步进行快速读取文件.

dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue1, ^{
        NSLog(@"queue1 run");
        sleep(1);
    });
   
    dispatch_async(queue1, ^{
        NSLog(@"queue2 run");
        sleep(1);
    });
    
    dispatch_barrier_async(queue1, ^{
        NSLog(@"====dispatch_barrier_async====");
    });
    
    dispatch_async(queue1, ^{
        NSLog(@"queue3 run");
        sleep(1);
    });
    
    dispatch_async(queue1, ^{
        NSLog(@"queue4 run");
        sleep(1);
    });

dispatch_group_t/dispatch_group_notify的使用,当结果依赖前面线程结果时可以使用group加notify达到目的

- (void)GCDTest{
    //创建并行队列
    dispatch_queue_t queue1 = dispatch_queue_create("com.test.queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("com.test.queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue3 = dispatch_queue_create("com.test.queue3", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue1, ^{
        NSLog(@"1 run");
        sleep(1);
    });
    
    dispatch_group_async(group, queue3, ^{
        NSLog(@"3 run");
        sleep(1);
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue2, ^{
        NSLog(@"2 run");
        sleep(1);
    });
    //里头的异步线程执行顺序不受group控制
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(10);
        NSLog(@"async run");
    });
    dispatch_group_leave(group);
    ///造成线程阻塞
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_async(group, queue3, ^{
        NSLog(@"4 run");
        sleep(3);
    });
    
    dispatch_group_async(group, queue3, ^{
        NSLog(@"5 run");
        sleep(1);
    });
        
    //group执行完毕调用
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           NSLog(@"main notify");
    });
}

执行结果:

2020-03-25 11:39:31.044035+0800 CodeReview[2125:182656] 1 run
2020-03-25 11:39:31.044036+0800 CodeReview[2125:182653] 2 run
2020-03-25 11:39:31.044051+0800 CodeReview[2125:182659] 3 run
2020-03-25 11:39:31.044486+0800 CodeReview[2125:182652] 5 run
2020-03-25 11:39:31.044509+0800 CodeReview[2125:182651] 4 run
2020-03-25 11:39:34.049393+0800 CodeReview[2125:182592] main notify
2020-03-25 11:39:41.044707+0800 CodeReview[2125:182654] async run

信号量处理多线程资源共享调用问题

- (void)semaphoreTest{
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        [self semaphoreChangeValue:1];
    });
   
    dispatch_block_t block2 = dispatch_block_create(0, ^{
        [self semaphoreChangeValue:2];
    });
    
    dispatch_block_t block3 = dispatch_block_create(0, ^{
        [self semaphoreChangeValue:3];
    });
    
    //信号量,处理多个异步事件实现同步执行的效果 保证每次只有一个线程在操作数据
    dispatch_async(dispatch_get_global_queue(0, 0), block1);
    
    dispatch_async(dispatch_get_global_queue(0, 0), block2);
    
    dispatch_async(dispatch_get_global_queue(0, 0), block3);
}

- (void)semaphoreChangeValue:(int)i{
        NSLog(@"block is %d",i);
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//信号量为0等待 信号量为1继续下一步
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            while (1) {
                self.num--;
                if(self.num<=0){
                    dispatch_semaphore_signal(semaphore);
                    return;
                }
                NSLog(@"%d %@",self.num,[NSThread currentThread]);
                sleep(5);
            }
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值