要售票首先要找一个变量来存放票数,但是直接放在一个变量里面改来改去的不方便,所以用一个NSObject对象的成员变量来存储。
Ticket.h:
</pre><pre name="code" class="cpp" style="color: rgb(255, 0, 0);">#import <Foundation/Foundation.h>
@interface Ticket : NSObject
+(Ticket *)sharedTicket;
//票数
@property (assign, atomic) NSInteger tickets;
@end
Ticket.m:
</pre><span style="font-size:14px;color:#ff0000;"></span><pre name="code" class="cpp" style="color: rgb(255, 0, 0);">#import "Ticket.h"
static Ticket *SharedInstance;
/*
做单例模型:
1、用静态变量来储存第一个被实例化的对象
2、重写allocWithZone方法,并使用diapatch_once_t
3、建立一个
*/
@implementation Ticket
//要实例化出来唯一的对象,要记录第一个实例化出来的对象
//只能在单线程中,如果用两个线程来跑,会出现两个
+ (id)allocWithZone:(struct _NSZone *)zone
{
//解决多线程问题,只能实例化一个对象副本
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SharedInstance = [super allocWithZone:zone];
});
//
// if (SharedInstance == nil) {
// SharedInstance = [super allocWithZone:zone];
// }
return SharedInstance;
}
//建立一个单例对象,便于调用
+(Ticket *)sharedTicket
{
//块代码里面只能使用外面的对象,不能修改外面的对象,要修改必须要加__block
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SharedInstance = [[Ticket alloc]init];
});
return SharedInstance;
}
@end
然后在MainViewController里面定义线程对票数进行操作:
MainViewController.h:
#import <UIKit/UIKit.h>
@interface MainViewController : UIViewController
@end
MainViewController.m:
#import "MainViewController.h"
#import "Ticket.h"
@interface MainViewController ()
@property (weak, nonatomic) UITextView *textView;
@property (strong, nonatomic) NSOperationQueue *queue;
@end
@implementation MainViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
#pragma mark - 追加当前文本框内容
- (void)appendContent:(NSString *)text
{
//1、取出textView内容
NSMutableString *str = [NSMutableString stringWithString:self.textView.text];
//2、将text追加至TextView内容的末尾
[str appendFormat:@"%@\n",text];
//3、使用追加后的文本,替换textView中的内容
[self.textView setText:str];
//4、将textView滚动至底部
NSRange range = NSMakeRange(str.length - 1, 1);
[self.textView scrollRangeToVisible:range];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Ticket *ticket1 = [[Ticket alloc]init];
// ticket1.tickets = 100;
// NSLog(@"%@",ticket1);
// Ticket *ticket2 = [[Ticket alloc]init];//alloc是分配空间,init是初始化属性
// NSLog(@"%d - %@",ticket2.tickets,ticket2);
// NSLog(@"%@",[Ticket sharedTicket]);
//使用内存地址实例化方法,所有实例化方法,最终都会调用此方法;
// [Ticket allocWithZone:<#(struct _NSZone *)#>]
UITextView *textView = [[UITextView alloc]initWithFrame:self.view.bounds];
[textView setEditable:NO];
[textView setText:@""];
[self.view addSubview:textView];
self.textView = textView;
[Ticket sharedTicket].tickets = 30;
NSInteger num = [Ticket sharedTicket].tickets;
// [self gcdSales];
self.queue = [[NSOperationQueue alloc]init];
// [self operationSales];
[self threadSales];
}
#pragma mark - NSThread卖票
- (void)threadSaleTicketWithName:(NSString *)name
{
//使用NSThread一定要用autoreleasepool包起来
//因为main.m里面的@autoreleasepool只处理主线程内存问题
//线程很多时,处理数据量很大时,很容易出问题
@autoreleasepool {
while (YES) {
@synchronized(self){
if ([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;
NSString *str = [NSString stringWithFormat:@"剩余票数%d 线程名称%@",[Ticket sharedTicket].tickets,name];
//更新UI
[self performSelectorOnMainThread:@selector(appendContent:) withObject:str waitUntilDone:YES];
}else{
NSLog(@"票卖完了%@ %@",name,[NSThread currentThread]);
break;
}
}
if ([name isEqualToString:@"thread-1"]) {
[NSThread sleepForTimeInterval:1.0f];
}else{
[NSThread sleepForTimeInterval:0.2f];
}
}
}
}
- (void)threadSales
{
[NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-1"];
[NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-2"];
}
#pragma mark - NSOperation卖票
- (void)operationSaleTicketWithName:(NSString *)name
{
while (YES) {
@synchronized(self){
if ([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;
NSString *str = [NSString stringWithFormat:@"剩余票数%d,线程名%@",[Ticket sharedTicket].tickets,name];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self appendContent:str];
}];
}else {
NSLog(@"票卖完了%@ %@",name,[NSThread currentThread]);
break;
}
}
if ([name isEqualToString:@"op-1"]) {
[NSThread sleepForTimeInterval:1.0f];
}else{
[NSThread sleepForTimeInterval:0.2f];
}
}
}
- (void)operationSales
{
//没有任务完成提示的功能
[self.queue setMaxConcurrentOperationCount:2];
[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-3"];
}];
[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-1"];
}];
//op-2被同步锁挡在外面了
[self.queue addOperationWithBlock:^{
[self operationSaleTicketWithName:@"op-2"];
}];
}
#pragma mark - GCD 买票
- (void)gcdSaleTicketWithName:(NSString *)name
{
while (YES) {
@synchronized(self) {
if ([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;
// 提示内容
NSString *str = [NSString stringWithFormat:@"剩余票数 %d, 线程名称 %@", [Ticket sharedTicket].tickets, name];
// 更新界面
dispatch_sync(dispatch_get_main_queue(), ^{
[self appendContent:str];
});
} else {
break;
}
}
// 模拟线程休眠
if ([name isEqualToString:@"gcd-1"]) {
[NSThread sleepForTimeInterval:1.0f];
} else {
[NSThread sleepForTimeInterval:0.2f];
}
}
}
- (void)gcdSales
{
//1、创建全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建两个线程分别卖票
// dispatch_async(queue, ^{
// [self gcdSaleTicketWithName:@"gcd-1"];
// });
// dispatch_async(queue, ^{
// [self gcdSaleTicketWithName:@"gcd-2"];
// });
// dispatch_async(queue, ^{
// [self gcdSaleTicketWithName:@"gcd-3"];
// });
//GCD中可以将一组相关联的操作,定义到一个群组中
//定义到群组中的操作可以当所有线程全部完成之后获得通知
//最典型的应用是小说阅读时候,所有的书下载完毕之后提醒用户下载
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self gcdSaleTicketWithName:@"gcd-1"];
});
dispatch_group_async(group, queue, ^{
[self gcdSaleTicketWithName:@"gcd-2"];
});
dispatch_group_async(group, queue, ^{
[self gcdSaleTicketWithName:@"gcd-3"];
});
//等的时间长的执行次数少,等待时间短的执行次数多
//群组任务完成接收通知
dispatch_group_notify(group, queue, ^{
NSLog(@"卖完了所有的票");
});
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end