623 lines
18 KiB
Objective-C
623 lines
18 KiB
Objective-C
//
|
||
// MyScriptTask.m
|
||
// nochange
|
||
//
|
||
// Created by mac on 2024/7/23.
|
||
//
|
||
|
||
#import "MyScriptTask.h"
|
||
|
||
#import "XSHelper.h"
|
||
#import "XSPhoneConfig.h"
|
||
#import "XSPhoneInfo.h"
|
||
|
||
#import "MyEventBus.h"
|
||
#import "XSHackIos.h"
|
||
|
||
|
||
#define total_ad_click 3
|
||
|
||
@interface MyScriptTask()
|
||
{
|
||
@private
|
||
// 单次app任务
|
||
NSString* _taskId;
|
||
NSString* _appId;
|
||
BOOL _lastAppTaskComplete;
|
||
NSTimeInterval _lastAppTaskTime;
|
||
NSTimeInterval _lastOcrTime;
|
||
int _lastAppTaskCount;
|
||
int _total;
|
||
BOOL _stop;
|
||
int _taskTimeout;
|
||
int _workType;
|
||
|
||
BOOL _lastTaskComplete; // 一个点击任务是否完成,按钮点击,广告展示,广告点击,广告回到页面
|
||
NSTimeInterval _lastTaskTime;
|
||
int _step; // 0 等待点击 1: 点击完成,2: 点击广告;3:广告点击完成,
|
||
|
||
int _adTouchBefore;
|
||
|
||
|
||
int _count;
|
||
int _safeTop;
|
||
|
||
NSMutableDictionary *_adShows;
|
||
NSString *_idfa;
|
||
CGFloat device_screen_width;
|
||
CGFloat device_screen_height;
|
||
|
||
int _loadCount;
|
||
}
|
||
|
||
@property (nonatomic, strong) NSMutableArray *inStack;
|
||
@property (nonatomic, strong) NSMutableArray *outStack;
|
||
@property (nonatomic, strong) dispatch_queue_t concurrentQueue;
|
||
|
||
@end
|
||
|
||
@implementation MyScriptTask
|
||
|
||
- (instancetype)init {
|
||
self = [super init];
|
||
if (self) {
|
||
_currentId = 0;
|
||
_concurrentQueue = dispatch_queue_create("com.xyzshell.adtask.queue", DISPATCH_QUEUE_CONCURRENT);
|
||
_inStack = [NSMutableArray array];
|
||
_outStack = [NSMutableArray array];
|
||
_adShows = [[NSMutableDictionary alloc] init];
|
||
|
||
_taskTimeout = 30;
|
||
_workType = 1;
|
||
|
||
_lastTaskComplete = YES;
|
||
_stop = NO;
|
||
_total = 0;
|
||
_count = 0;
|
||
_lastAppTaskComplete = YES;
|
||
_lastAppTaskTime = [self getCurTime];
|
||
_lastAppTaskCount = 0;
|
||
_safeTop = 20;
|
||
_adTouchBefore = 3;
|
||
_taskId = @"0";
|
||
_appId = @"";
|
||
_loadCount = 0;
|
||
_lastOcrTime = [self getCurTime];
|
||
NSLog(@"OhNo Server Start %@", [[XSPhoneConfig sharedInstance] IPhoneName]);
|
||
CGFloat screen_scale = [[UIScreen mainScreen] scale];
|
||
self->device_screen_width = [UIScreen mainScreen].bounds.size.width * screen_scale;
|
||
self->device_screen_height = [UIScreen mainScreen].bounds.size.height * screen_scale;
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void) showStatus: (NSString *)data {
|
||
[[MyEventBus sharedInstance] postEvent:@"UpdateStatus" withObject:data];
|
||
}
|
||
|
||
- (void) start {
|
||
_stop = NO;
|
||
|
||
[self runStartApp];
|
||
}
|
||
|
||
- (void) stop {
|
||
_stop = YES;
|
||
}
|
||
|
||
- (int64_t)nextId {
|
||
@synchronized (self) {
|
||
self.currentId++;
|
||
return self.currentId;
|
||
}
|
||
}
|
||
|
||
- (int)enqueue:(id)item {
|
||
int load = 0;
|
||
dispatch_barrier_async(self.concurrentQueue, ^{
|
||
NSDictionary *dic = item;
|
||
NSLog(@"-ad log- add task:\n %@", dic);
|
||
NSMutableDictionary *taskDic = [[NSMutableDictionary alloc] initWithDictionary:dic];
|
||
[taskDic setValue:@([self nextId]) forKey:@"_id"];
|
||
[self->_adShows setValue: dic[@"ad_time"] forKey:dic[@"adId"]];
|
||
self ->_adTouchBefore = [dic[@"ad_touchBeforeMs"] intValue];
|
||
[self.inStack addObject:taskDic];
|
||
self->_count++;
|
||
[self showStatus:[NSString stringWithFormat: @"队列任务数量:%d, loads:%@", self->_count, dic[@"ad_loads"]]];
|
||
});
|
||
return load;
|
||
}
|
||
|
||
- (id)dequeue {
|
||
__block id result = nil;
|
||
dispatch_barrier_sync(self.concurrentQueue, ^{
|
||
if ([self.outStack count] == 0) {
|
||
NSUInteger count = [self.inStack count];
|
||
while (count > 0) {
|
||
[self.outStack addObject:[self.inStack lastObject]];
|
||
[self.inStack removeLastObject];
|
||
count--;
|
||
}
|
||
}
|
||
|
||
if ([self.outStack count] > 0) {
|
||
result = [self.outStack lastObject];
|
||
[self.outStack removeLastObject];
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
|
||
- (void) reset {
|
||
_lastTaskComplete = YES;
|
||
_lastTaskTime = [self getCurTime];
|
||
}
|
||
|
||
|
||
|
||
- (BOOL) isRun {
|
||
return !_stop;;
|
||
}
|
||
|
||
- (void) run {
|
||
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
|
||
dispatch_async(queue, ^{
|
||
self.timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(proc) userInfo:nil repeats:YES];
|
||
//timer 加入runloop
|
||
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
|
||
//开启runloop
|
||
[[NSRunLoop currentRunLoop] run];
|
||
});
|
||
}
|
||
|
||
- (void) completeTask {
|
||
_lastTaskComplete = YES;
|
||
_lastTaskTime = [self getCurTime];
|
||
_lastAppTaskCount++;
|
||
}
|
||
|
||
- (void) adClosed: (ScriptTask *)task {
|
||
[self showStatus:[NSString stringWithFormat:@"关闭:%@", task->adId]];
|
||
[self completeTask];
|
||
}
|
||
|
||
- (void) loadIncrCount {
|
||
self->_loadCount = self->_loadCount + 1;
|
||
}
|
||
|
||
-(void) restart {
|
||
dispatch_barrier_sync(self.concurrentQueue, ^{
|
||
_loadCount = 0;
|
||
_lastTaskComplete = YES;
|
||
_lastTaskTime = [self getCurTime];
|
||
_lastAppTaskComplete = YES;
|
||
_lastAppTaskTime = [self getCurTime];
|
||
_lastAppTaskCount = 0;
|
||
[self.outStack removeAllObjects];
|
||
[self.inStack removeAllObjects];
|
||
[_adShows removeAllObjects];
|
||
_count = 0;
|
||
});
|
||
}
|
||
|
||
- (void) killApp: (NSString *)pkgName {
|
||
XSKillAppByName(pkgName);
|
||
}
|
||
|
||
- (void) runStartApp {
|
||
[self restart];
|
||
NSLog(@"-ad log- runStartApp");
|
||
getChangeInfo(_idfa, ^(NSDictionary *dic) {
|
||
if (!dic || [dic isEqual: [NSNull null]]) {
|
||
NSLog(@"get change info dic is null");
|
||
return;
|
||
}
|
||
NSDictionary *workInfo = dic[@"workerInfo"];
|
||
if (!workInfo || [workInfo isEqual:[NSNull null]]) {
|
||
NSLog(@"-ad log- not have task");
|
||
return;
|
||
}
|
||
NSNumber *workType = workInfo[@"workType"];
|
||
self->_workType = [workType intValue];
|
||
if (self->_workType <= -1) {
|
||
NSLog(@"-ad log- not have task");
|
||
return;
|
||
}
|
||
self->_taskId = dic[@"id"];
|
||
self->_idfa = dic[@"idfa"];
|
||
|
||
saveChangeDataFile(dic);
|
||
// NSNumber *taskTimeout = workInfo[@"taskTimeout"];
|
||
|
||
self->_appId = dic[@"appId"];
|
||
NSLog(@"-ad log- change %@", self->_appId);
|
||
NSDictionary *safeAreaInsets = dic[@"safeAreaInsets"];
|
||
if (safeAreaInsets) {
|
||
NSString *top = safeAreaInsets[@"top"];
|
||
if (top) {
|
||
self->_safeTop = [top intValue];
|
||
}
|
||
}
|
||
// 获取任务,开始启动
|
||
//ScriptTask oldtask = {
|
||
// .appId = self->_appId
|
||
//};
|
||
[self killApp:self->_appId];
|
||
// [self runStopScript:&oldtask];
|
||
|
||
// 获取任务,开始启动
|
||
//ScriptTask task = {
|
||
// .appId = self->_appId,
|
||
//};
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
XSRemoteUnlock();
|
||
usleep(5000);
|
||
XSRemoteUnlock();
|
||
});
|
||
|
||
//[self runSystemRestartScript];
|
||
|
||
// TODO: 备份数据
|
||
NSString *appInfoPath = XSGetAppInfoPath(self->_appId);
|
||
NSDictionary *appInfo = XSGetAppInfo(appInfoPath);
|
||
|
||
XSClearAll(self->_appId);
|
||
|
||
XSSaveAppInfo(appInfo, appInfoPath);
|
||
|
||
|
||
|
||
|
||
[[UIApplication sharedApplication] performSelectorOnMainThread:@selector(beginIgnoringInteractionEvents) withObject:nil waitUntilDone:YES];
|
||
[[UIApplication sharedApplication] performSelectorOnMainThread:@selector(endIgnoringInteractionEvents) withObject:nil waitUntilDone:YES];
|
||
|
||
//[self runStartScript:&task];
|
||
XSBringAppForeground(self->_appId);
|
||
}, ^(NSError *err) {
|
||
|
||
});
|
||
|
||
}
|
||
- (void) proc {
|
||
@try {
|
||
[self _proc];
|
||
} @catch (NSException *exception) {
|
||
NSLog(@"-ad- erro");
|
||
}
|
||
}
|
||
- (void) _proc {
|
||
if (_stop) {
|
||
return;
|
||
}
|
||
NSLog(@"start proc task........");
|
||
|
||
if (_workType < 1) {
|
||
if (_count > 1 && ([self getCurTime] - _lastAppTaskTime) > 6) {
|
||
[self runStartApp];
|
||
} else if (([self getCurTime] - _lastAppTaskTime) > 20){
|
||
[self runStartApp];
|
||
}
|
||
|
||
return;
|
||
}
|
||
if (_workType == 4) {
|
||
if (_lastTaskComplete == YES) {
|
||
_lastTaskComplete = NO;
|
||
_lastTaskTime = [self getCurTime];
|
||
ScriptTask sctask = {
|
||
.idfa = _idfa,
|
||
.appId = _appId,
|
||
.ad = true,
|
||
.adId = @""
|
||
};
|
||
AdTaskLogData logData = {
|
||
.title = @"广告展示",
|
||
.idfa = _idfa,
|
||
.appid = _appId,
|
||
.adid = @"",
|
||
.message = @"",
|
||
.level = 1,
|
||
.ecpm = @(0)
|
||
};
|
||
self->_adTouchBefore = 10000;
|
||
[[MyScriptTask sharedInstance] saveAdTaskLog:&logData];
|
||
[[MyScriptTask sharedInstance] runAdTouchScript:&sctask];
|
||
return;
|
||
} else {
|
||
if (([self getCurTime] - _lastTaskTime) > 18) {
|
||
[self runStartApp];
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (self->_loadCount > 0) {
|
||
if (_lastAppTaskCount >= self->_loadCount) {
|
||
sleep(2);
|
||
[self runStartApp];
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (([self getCurTime] - _lastAppTaskTime) > 80) {
|
||
[self runStartApp];
|
||
return;
|
||
}
|
||
if (_lastTaskComplete == NO) {
|
||
// 上次任务没有执行完,30秒不等待,继续下一个任务
|
||
if (_lastTaskTime && ([self getCurTime] - _lastTaskTime) < _taskTimeout) {
|
||
NSLog(@"last task running");
|
||
return;
|
||
}
|
||
}
|
||
|
||
@try {
|
||
id task = [self dequeue];
|
||
if (task) {
|
||
_lastTaskComplete = NO;
|
||
if (_lastAppTaskComplete == YES) {
|
||
_lastAppTaskComplete = NO;
|
||
}
|
||
// 处理任务
|
||
_lastTaskTime = [self getCurTime];
|
||
NSMutableDictionary *taskData = task;
|
||
NSLog(@"pad roc task: \n%@", taskData);
|
||
NSString *type = taskData[@"type"];
|
||
if ([type isEqual:@"click_btn"]) {
|
||
NSLog(@"ad proc task: click_btn - begin");
|
||
[self showStatus:[NSString stringWithFormat:@"点击:%@", taskData[@"adId"]]];
|
||
NSNumber *x = taskData[@"x"];
|
||
NSNumber *y = taskData[@"y"];
|
||
FixedPoint point = {
|
||
.x = [x intValue],
|
||
.y = [y intValue]
|
||
};
|
||
ScriptTask st = { .btnLoc = &point, .appId=_appId };
|
||
[self runAdShowScript:&st];
|
||
}
|
||
} else {
|
||
if (_lastTaskTime && ([self getCurTime] - _lastTaskTime) > 20) {
|
||
[self runStartApp];
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
@catch (NSException *exception) {
|
||
NSLog(@"ad proc ex name: %@", exception.name);
|
||
NSLog(@"ad proc ex reason: %@", exception.reason);
|
||
[self completeTask];
|
||
}
|
||
@finally {
|
||
NSLog(@"ad proc end");
|
||
}
|
||
}
|
||
|
||
- (BOOL) replaceScript: (NSString *)tplName scriptName: (NSString *)scriptName callback: (ScriptReplaceCallback)callback {
|
||
NSString *scriptTpl = [self readScript:tplName];
|
||
if (scriptTpl == nil) {
|
||
NSLog(@"ad read script is nil");
|
||
return NO;
|
||
}
|
||
NSLog(@"ad tpl script: %@", tplName);
|
||
NSMutableString *newScript = [NSMutableString stringWithString:scriptTpl];
|
||
callback(newScript);
|
||
// NSLog(@"ad new script: %@", newScript);
|
||
return [self saveScript:[newScript copy] name:scriptName];
|
||
}
|
||
|
||
- (void) runAdShowScript: (ScriptTask *) taskData {
|
||
// 1. 写入按钮点击脚本
|
||
NSLog(@"ad runAdShowScript");
|
||
|
||
NSString *curApp = XSFrontMostAppId();
|
||
if(![self->_appId isEqual:curApp]) {
|
||
XSBringAppForeground(self->_appId);
|
||
sleep(2);
|
||
}
|
||
usleep(5000);
|
||
int x = taskData->btnLoc->x * 2;
|
||
int y = taskData->btnLoc->y;
|
||
int ny = y * 2 + (self->_safeTop / 2) - 10;
|
||
|
||
|
||
/*
|
||
BOOL flag = [self replaceScript:SCRIPT_TOUCH_BTN_TPL scriptName:SCRIPT_TOUCH_BTN callback:^(NSMutableString *newScript) {
|
||
|
||
|
||
[MyHelper replaceMulStr:newScript oldStr:@"local btn_x = __BTN_X__" newStr:[NSString stringWithFormat:@"local btn_x = %d", x]];
|
||
[MyHelper replaceMulStr:newScript oldStr:@"local btn_y = __BTN_Y__" newStr:[NSString stringWithFormat:@"local btn_y = %d", ny]];
|
||
[MyHelper replaceMulStr:newScript oldStr:@"__APPID__" newStr:taskData->appId];
|
||
|
||
}];
|
||
|
||
|
||
// 2. 运行脚本
|
||
if (flag) {
|
||
NSLog(@"save script ok: %@", SCRIPT_TOUCH_BTN);
|
||
NSString *res = [self runScript:SCRIPT_TOUCH_BTN];
|
||
if (res) {
|
||
NSLog(@"ad run script success: %@", SCRIPT_TOUCH_BTN);
|
||
[self showStatus:[NSString stringWithFormat:@"运行点击按钮成功,%@",res]];
|
||
} else {
|
||
[self showStatus:[NSString stringWithFormat:@"运行点击按钮失败,%@",res]];
|
||
}
|
||
|
||
} else {
|
||
NSLog(@"save script error: %@", SCRIPT_TOUCH_BTN);
|
||
[self showStatus:@"保存按钮点击脚本失败"];
|
||
}
|
||
//*/
|
||
}
|
||
|
||
- (void) runStopScript: (ScriptTask *) taskData {
|
||
|
||
BOOL flag = [self replaceScript:SCRIPT_KILL_ALL_TPL scriptName:SCRIPT_KILL_ALL callback:^(NSMutableString *newScript) {
|
||
[XSHelper replaceMulStr:newScript oldStr:@"__APPID__" newStr:taskData->appId];
|
||
}];
|
||
if (flag) {
|
||
// 结束所有相关app
|
||
NSString *res = [self runScript:SCRIPT_KILL_ALL];
|
||
if (res) {
|
||
NSLog(@"-ad -log %@",res);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
- (int) runAdTouchScript: (ScriptTask *) taskData {
|
||
if (!taskData->ad) {
|
||
// 没有广告展示
|
||
NSLog(@"-ad log- touch btn No ad");
|
||
[self completeTask];
|
||
[self showStatus: [NSString stringWithFormat:@"广告展示失败:%@", taskData->adId]];
|
||
return 0;
|
||
}
|
||
_lastTaskTime = [self getCurTime];
|
||
/*
|
||
NSLog(@"-ad log- run ad touch script: %@", SCRIPT_TOUCH_AD);
|
||
BOOL flag = [self replaceScript:SCRIPT_TOUCH_AD_TPL scriptName:SCRIPT_TOUCH_AD callback:^(NSMutableString *newScript) {
|
||
[MyHelper replaceMulStr:newScript oldStr:@"__APPID__" newStr:taskData->appId];
|
||
[MyHelper replaceMulStr:newScript oldStr:@"__BEFORE__" newStr:[NSString stringWithFormat:@"%d", self->_adTouchBefore]];
|
||
}];
|
||
if (flag) {
|
||
NSString *res = [self runScript:SCRIPT_TOUCH_AD];
|
||
if (res) {
|
||
NSLog(@"sc res: %@", res);
|
||
}
|
||
|
||
}
|
||
*/
|
||
[self runTouchAdJs:self->_adTouchBefore];
|
||
int show_ms = 5000;
|
||
if ([_adShows objectForKey:taskData->adId]) {
|
||
NSNumber *shows = _adShows[taskData->adId];
|
||
show_ms = [shows intValue];
|
||
}
|
||
[self showStatus: [NSString stringWithFormat:@"广告展,%@", taskData->adId]];
|
||
// 点击广告
|
||
return show_ms;
|
||
}
|
||
|
||
- (void) runStartScript: (ScriptTask *) taskData {
|
||
// 启动app 开始任务
|
||
// 清除队列里面所有任务, 重置状态
|
||
[self showStatus:@"start new task"];
|
||
_lastTaskTime = [self getCurTime];
|
||
|
||
BOOL flag = [self replaceScript:SCRIPT_START_TPL scriptName:SCRIPT_START callback:^(NSMutableString *newScript) {
|
||
[XSHelper replaceMulStr:newScript oldStr:@"__APPID__" newStr:taskData->appId];
|
||
}];
|
||
if (flag) {
|
||
NSString *res = [self runScript:SCRIPT_START];
|
||
if (res) {
|
||
NSLog(@"-ad- start task:%@", res);
|
||
}
|
||
}
|
||
}
|
||
|
||
- (void) runSystemRestartScript {
|
||
NSString *res = [self runScript:@"close_not_now.lua"];
|
||
if (res) {
|
||
NSLog(@"kill a all");
|
||
}
|
||
}
|
||
|
||
- (NSString *) runScript: (NSString *)script {
|
||
/*
|
||
NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:8080/control/start_playing?path=/%@", script];
|
||
[XSHelper doGET:url withCallback:^(NSData* _data) {
|
||
if (_data) {
|
||
NSString *res = [MyHelper data2str:_data];
|
||
[self showStatus:[NSString stringWithFormat: @"运行脚本成功,%@", res]];
|
||
}
|
||
} withError:^(NSError *err) {
|
||
NSLog(@"run autouch script error");
|
||
}];
|
||
|
||
NSLog(@"run autouch script error");
|
||
*/
|
||
return nil;
|
||
}
|
||
|
||
- (NSString *) stopScript: (NSString *)script {
|
||
/*
|
||
NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:8080/control/stop_playing?path=/%@", script];
|
||
[_http doGET:url callback:^(NSData* _data) {
|
||
if (_data) {
|
||
NSString *res = [MyHelper data2str:_data];
|
||
[self showStatus:[NSString stringWithFormat: @"停止脚本成功,%@", res]];
|
||
}
|
||
}];
|
||
NSLog(@"run autouch script error");
|
||
*/
|
||
return nil;
|
||
}
|
||
|
||
- (NSString *) readScript: (NSString *)name {
|
||
NSString *scriptPath = [self getScriptPath:name];
|
||
return [XSHelper readFileText:scriptPath];
|
||
}
|
||
|
||
- (BOOL) saveScript: (NSString *)script name:(NSString *)name {
|
||
NSString *scriptPath = [self getScriptPath:name];
|
||
return [XSHelper saveFile:scriptPath text:script];
|
||
}
|
||
|
||
- (NSString *) getScriptPath: (NSString *) name {
|
||
return [NSString stringWithFormat:@"/var/mobile/Library/AutoTouch/Scripts/%@", name];
|
||
}
|
||
|
||
- (NSTimeInterval) getCurTime {
|
||
// 获取当前日期和时间
|
||
NSDate *now = [NSDate date];
|
||
return [now timeIntervalSince1970];
|
||
}
|
||
|
||
- (void) logServer: (NSString *) info title: (NSString *)title {
|
||
|
||
}
|
||
|
||
- (void) saveAdTaskLog: (AdTaskLogData *)data {
|
||
data->taskId = _taskId;
|
||
data->iphoneId = [[XSPhoneConfig sharedInstance] IPhoneName];
|
||
data->ipAddr = [[XSPhoneInfo sharedInstance] IPAddress];
|
||
pushAdTaskLog(data);
|
||
}
|
||
|
||
- (NSString*) statusStr {
|
||
NSString *runStatusStr = _stop ? @"已停止": @"运行中";
|
||
return [NSString stringWithFormat:@"%@", runStatusStr];
|
||
}
|
||
- (NSString*) appid {
|
||
return _appId;
|
||
}
|
||
|
||
- (void) runTouchAdJs: (NSInteger) befor {
|
||
/*
|
||
JsRunner *runner = [[JsRunner alloc] init];
|
||
NSString *tmpCode = @"var beforeTouch = %@;"
|
||
"var cur_app_id = \"%@\";"
|
||
"var screen_w = %@;"
|
||
"var screen_h = %@;";
|
||
NSString *code = [NSString stringWithFormat:tmpCode, @(befor / 1000), [self appid], @(self->device_screen_width), @(self->device_screen_height)];
|
||
NSLog(@"runTouchAdJs:%@", code);
|
||
[runner runCode:code];
|
||
[runner runJS:@"/var/mobile/Library/AutoTouch/Scripts/touch_ad.js" timeout:50 completion:^(JSValue *result, BOOL timedOut) {
|
||
NSLog(@"runTouchAdJs end");
|
||
}];
|
||
*/
|
||
}
|
||
|
||
+(instancetype)sharedInstance
|
||
{
|
||
static MyScriptTask* _sharedInstance = nil;
|
||
static dispatch_once_t oncePredicate;
|
||
dispatch_once (&oncePredicate, ^{
|
||
_sharedInstance = [[MyScriptTask alloc] init];
|
||
});
|
||
return _sharedInstance;
|
||
}
|
||
|
||
@end
|