I am writing an application which will do a multiple task simultaneously. One particular task is to do a job once every 200 ms. To achieve this, I am using two methods calling each other. The first method just calls the second and the second method calls the first with a delay using dispatch_after().
After a few iterations(300-400 times) the block in dispatch_after is not executed after 200 ms. It takes ~5-10 seconds before the block is executed. Please let me know the reason for the behaviour(delay). I also tried NSThread (sleepForTimeInterval:) and I am facing the same problem there too.I am stuck. Please help me.
The code is given below.
Screen.h
#import <Foundation/Foundation.h>
#interface Screen : NSObject
-(void) firstMethod;
-(void) secondMethod;
#end
Screen.m
#import "Screen.h"
#implementation Screen
int i=0;
dispatch_queue_t another_queue;
dispatch_time_t pop_time;
-(Screen*) init {
self = [super init];
if (self) {
another_queue = dispatch_queue_create("com.test.timer.2", NULL);
}
return self;
}
-(void) firstMethod {
i++;
NSLog(#"i value : %d",i);
[self secondMethod];
}
-(void) secondMethod {
pop_time = dispatch_time(DISPATCH_TIME_NOW, 200 * NSEC_PER_MSEC);
dispatch_after(pop_time, another_queue, ^(void){
[self firstMethod];
});
}
#end
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Screen.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
Screen* screen = [[Screen alloc] init];
dispatch_queue_t first_queue = dispatch_queue_create("com.test.timer", NULL);
dispatch_block_t blk =^(void) {
[screen firstMethod];
};
dispatch_async(first_queue, blk);
}
#end
Is your app in the foreground when this occurs? If not, you may simply be seeing App Nap kicking in. One of the things it does is throttle timers in background apps.
One possible effect is "Timer Coalescing" and "App Nap".
Related: this question: Have you noticed that dispatch_after runs ~10% too slow on iOS devices?.
If this is actually the cause of your problem, you can fix it using a timer, either NSTimer, or use our own implementation, based on dispatch lib, where you can control the exact behavior. See also: an implementation of a timer on Gist: RXTimer
Edit:
On Mac OS X and when App Nap kicks in, it seems we have no control over the delay respectively that huge "leeway".
I had the same issue on OS X. My delay was more than 1000% off of the specified delay.
Turning off App Nap system wide solved the leeway:
defaults write NSGlobalDomain NSAppSleepDisabled -bool YES
Related
I am quite new to react native and and the bridging mechanism with native code, especially when the framework has delegates. Assume I am trying to bridge the following framework:
#protocol BRPtouchNetworkDelegate;
#class PLNetworkModule;
#interface BRPtouchNetworkManager : NSObject <NSNetServiceBrowserDelegate,NSNetServiceDelegate>
#property(retain, nonatomic) NSMutableArray* registeredPrinterNames;
#property(assign, nonatomic) BOOL isEnableIPv6Search;
- (int)startSearch: (int)searchTime;
- (NSArray*)getPrinterNetInfo;
- (BOOL)setPrinterNames:(NSArray*)strPrinterNames;
- (BOOL)setPrinterName:(NSString*)strPrinterName;
- (id)initWithPrinterNames:(NSArray*)strPrinterNames;
- (id)initWithPrinterName:(NSString*)strPrinterName;
#property (nonatomic, assign) id <BRPtouchNetworkDelegate> delegate;
#end
#protocol BRPtouchNetworkDelegate <NSObject>
-(void) didFinishSearch:(id)sender;
#end
The following is the bridge module I implemented:
RCTBRPtouchNetworkManager.h
#import <React/RCTBridgeModule.h>
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#interface RCTBRPtouchNetworkManager : NSObject <RCTBridgeModule, BRPtouchNetworkDelegate>
#end
RCTBRPtouchNetworkManager.m
#import "RCTBRPtouchNetworkManager.h"
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#import <React/RCTLog.h>
#implementation RCTBRPtouchNetworkManager {
BRPtouchNetworkManager *_networkManager;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(#"Pretending to create an event %# at %#", name, location); //a dummy method to test the bridge
}
RCT_EXPORT_METHOD(startSearchWithTimeout:(int)time) {
RCTLogInfo(#"Bridge started search with time %d", time);
_networkManager = [[BRPtouchNetworkManager alloc] init];
_networkManager.delegate = self; //I'm setting delegate here
_networkManager.isEnableIPv6Search = NO;
NSString * path = [[NSBundle mainBundle] pathForResource:#"PrinterList" ofType:#"plist"];
if( path )
{
NSDictionary *printerDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *printerList = [[NSArray alloc] initWithArray:printerDict.allKeys];
[_networkManager setPrinterNames:printerList];
} else {
RCTLogInfo(#"PrinterList path not found");
}
// Start printer search
[_networkManager startSearch: 5.0];
}
- (void)didFinishSearch:(id)sender {
NSLog(#"didFinishedSearch"); //this delegate method is not called
}
#end
I can easily call the dummy method and see the results in the logs. However, the delegate method didFinishSearch() is never called. I call this from javascript as follows:
componentDidMount() {
let networkManager = NativeModules.BRPtouchNetworkManager;
networkManager.startSearchWithTimeout(5.0);
}
I there something I am missing? Am I implementing delegate properly? Is this kind of functionality even possible (can't seem to not since the delegate method was used by iOS community for a long time). Your help is much appreciated.
EDIT
I found that adding the following to my bridge manager file made the delegate to fire (thanks to this post)
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
However, even though this solves the problem, I'd like a more technical understanding on what is going on here since I can't seem to exactly grasp it. Thank you
I know this isn’t an an answer to the post but for the bit where you’ve asked for a more technical understanding - dispatch_get_main_queue(); puts the delegate method responses on to the main thread. Since JS is single threaded any process on the background thread won’t be visible to it.
So I got this code inside the update() function/method that tracks the game session in seconds (this is for achievement purposes) If someone has a better way of doing this, I'm all ears:
-(void)update:(NSTimeInterval)currentTime
{
if (pauseTimer == NO)
{
millisecondsTimer++;
if(millisecondsTimer == 60) //Remember that SK runs at 60 FPS default which means 60 equals 1 second.
{
millisecondsTimer = 0; //Reset the counter to 0 to begin again.
_secondsTracker++; //Add 1 second to this property.
}
}
else
{
NSLog(#"Timer paused.");
}
}
I want to take the _secondsTracker & save its value right before someone turns off the game completely (as in double clicks home button & swipes the game window up & away). The way I have it working now is using the appDelegate like so:
AppDelegate.h
#import <UIKit/UIKit.h>
BOOL pauseTimer;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "GameScene.h" //For sessionTracker property.
#import "GameData.h" //To call upon gdTotalSessionTime property.
#interface AppDelegate ()
#end
#implementation AppDelegate
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
/*Override point for customization after application launch.*/
return YES;
}
-(void)applicationWillResignActive:(UIApplication *)application
{
/*Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.*/
/*Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.*/
pauseTimer = YES; //Will stop the secondsTimer accumulating value.
}
-(void)applicationDidEnterBackground:(UIApplication *)application
{
/*Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.*/
/*If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.*/
//Will add secondsTracker value to the total (gdTotalSessionTIme).
GameScene *gS = [[GameScene alloc] init];
[GameData sharedGameData].gdTotalSeconds = [GameData sharedGameData].gdTotalSeconds + [gS.secondsTracker]; //Supposed to add the secondsTracker value to the gdTotalSeconds value.
[[GameData sharedGameData] save]; //This saves all the data to a file in app.
NSLog(#"THIS SESSION TIME: %ld seconds", gS.secondsTracker);
}
-(void)applicationWillEnterForeground:(UIApplication *)application
{
/*Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.*/
}
-(void)applicationDidBecomeActive:(UIApplication *)application
{
/*Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.*/
pauseTimer = NO; //Will resume the secondsTimer value accumulation.
}
-(void)applicationWillTerminate:(UIApplication *)application
{
/*Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.*/
}
The line of code that's giving me problems is this one:
[GameData sharedGameData].gdTotalSeconds = [GameData sharedGameData].gdTotalSeconds + gS.secondsTracker;
More specifically gS.secondsTracker. Xcode keeps wanting to replace it into this:
*([GameData sharedGameData].gdTotalSeconds + gS.secondsTracker);
Doing so, nothing adds up the way I want it too & I know for a fact the GameData class is working correctly & saving other data to a file it created. Someone please tell me what these *() are?
While I advised that you can probably provide a singleton accessor to get hold of the currently running instance of GameScene it's not quite a singleton as you will want to allow GameScene objects to be created as normal. What is required is simply accessing the "current" instance, with something like:
header
#interface GameScene : SKScene
...
+ (GameScene *)currentGameScene;
...
#end
implementation
static GameScene *_instance = nil;
#interface GameScene
- (instancetype)init...
{
if (self) ...
_instance = self;
return self;
}
- (void)dealloc
{
if (self == _instance)
_instance = nil;
}
+ (GameScene *)currentGameScene
{
return _instance;
}
I am relatively new to Cocoa programming. Basically, I want to send a message within a method in my Document class to an intense of a class (that inherits from NSView) that I have initialised as a property in the #interface of the Document class.
Here is the simplified version:
///////////////////////////KOZDocument.h///////////////////////////
#import <Cocoa/Cocoa.h>
#import "KOZOtherClass.h"
#interface KOZDocument : NSDocument
#property (assign) IBOutlet KOZOtherClass *otherClassInstance; //this would be connected to the relevant CustomView in the IB
#end
///////////////////////////KOZDocument.m///////////////////////////
#import "KOZDocument.h"
#implementation KOZDocument
- (id)init
{
self = [super init];
if (self) {
// I want to send a message to otherClassInstance from some method e.g. init
NSLog(#"INITIALISING");
[[self otherClassInstance] printMessage];// this is the message I want to work but which doesn't (even though i don't any errors)
//sending the message to a locally initiated instance works but I don't want to use a local instance because i want to connect it to a CustomView in IB
KOZOtherClass *otherClassLocalInstance = [[KOZOtherClass alloc] init];
[otherClassLocalInstance printMessage];
}
return self;
}
//.….
///////////////////////////KOZOtherClass.h///////////////////////////
#import <Foundation/Foundation.h>
#interface KOZOtherClass : NSView
- (void) printMessage;
#end
///////////////////////////KOZOtherClass.m///////////////////////////
#import "KOZOtherClass.h"
#implementation KOZOtherClass
- (void) printMessage{
NSLog(#"This method can be called!!");
}
#end
/////////////////////////////////////////////////////////////////////
The same methodology works for all the native Cocoa objects but not for mine.
Can anyone tell me what I'm doing wrong?
Here is the context of why i want to do this:
I am building an app that plays a video using the AVFoundation. I have an animation I want to trigger in an NSView when the playback reaches a particular part in the video (e.g. after 2 seconds). I am adapting Apple's AVSimplePlayer and using the time observer to get the position of the playhead. The time observer executes the code inside a block for every given time interval. In this block I want to send a message to my animation view to trigger the animation when the time is more that 5 seconds for example.
In -init of your objects the Interface Builder connections are not set yet, the loading mechanism can't set those before your object is initialized.
Instead you want to overwrite the -awakeFromNib method like so:
- (void)awakeFromNib {
[[self otherClassInstance] printMessage];
}
-awakeFromNib is guaranteed to be called after the connections have been made. Depending on the exact implementation you may also need to guard against that code being executed twice, for example by having a boolean instance variable didWake that you check/set in that method.
I'm messing around with using objects to launch background threads, however when I call an objects method to call the method that will spawn a background thread, nothing happens. I'm a bit puzzled as to why, and it looks like the -init function isn't even being called. Anyways, here's what I have:
ViewController.h
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
#property(nonatomic, strong) Thread* threadedObject;
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "Threader.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_threadedObject = [[Threader alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender {
NSLog(#"Clicked.");
[_threadedObject RunInBackground];
}
#end
Threader.h
#import <Foundation/Foundation.h>
#interface Threader : NSObject
#property(nonatomic) bool IsFinishedRunning;
#property(nonatomic) bool IsThreading;
//Constructor and Destructor
-(id)init;
-(void)dealloc;
-(void)RunInBackground;
-(void)WaitForTenSeconds;
#end
Threader.m
#import "Threader.h"
#implementation Threader
//constructor
-(id)init{
[super init];
if(self != nil)
{
_IsFinishedRunning = NO;
_IsThreading = NO;
}
return self;
}
//destructor
-(void)dealloc{
[super dealloc];
}
//Runs a thread in the background
-(void)RunInBackground{
NSLog(#"Initiating thread...");
[self performSelectorInBackground:#selector(WaitForTenSeconds) withObject:nil];
}
//Waits for 10 seconds, then sets IsFinishedRunning to YES
-(void)WaitForTenSeconds{
NSLog(#"Starting to run in the background.");
_IsThreading = YES;
sleep(10);
_IsFinishedRunning = YES;
NSLog(#"Finished running in the background.");
}
#end
When I run the program, this is my output(I clicked the button a few times)
2013-05-17 15:30:57.267 ThreadedObjects Clicked.
2013-05-17 15:30:59.003 ThreadedObjects Clicked.
2013-05-17 15:30:59.259 ThreadedObjects Clicked.
2013-05-17 15:30:59.443 ThreadedObjects Clicked.
2013-05-17 15:30:59.675 ThreadedObjects Clicked.
I should be getting messages telling me that the Threader object was created, and that it is preparing to launch a background thread, that the thread has been spawned and then after 10 seconds, that the thread is done running.
So, where's my glaring obvious error?
init isn't a constructor, it's for setup after construction. You need the class object to create an instance before you can send init, and, most importantly, you need to assign the results to your variable.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
threadedObject = [[Threader alloc] init];
}
You can't send alloc to an object that's not a class; instances don't respond to it. The only reason that this isn't crashing is that globals are initialized to 0/NULL/nil, and [nil someMessage] does nothing.
Not assigning the results to your variable is the same as:
int x = 0;
x + 10;
There's no change to x's value.
Additionally, you don't seem to have an ivar there, just a global variable. Ivars need to go into a curly-brace block at the head of the #implementation:
#implementation Threader
{
Threader * threadedObject;
}
// etc...
You never alloc the object.............
Also, this is curious:
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
Threader* threadedObject;
Where exactly did you declare the threadedObject? Like above? Use an iVar
or, better, a property for it!
A couple of reactions:
Show us where your definition and alloc/init of threadedObject.
I'm not sure what business problem you're trying to solve, but this smells like the precursor of some custom NSOperation solution. Operation queues are ideally suited for these sorts of implementations.
I'd be inclined to subclass NSOperation when trying to do something like this. See the custom NSOperation object in the Concurrency Programming Guide.
I'd suggest using camelCase for your method and variable names.
If you say with this, I'd steer you away from the "thread" name, as it might imply that you're doing something with NSThread, which you're not.
I have created a method that is running in new thread.
[NSThread detachNewThreadSelector:#selector(setmostpopularReq:) toTarget:self withObject:mostPopulerstring];
After completed this method i send all data to main thread.
[self performSelectorOnMainThread:#selector(getmostpopularResponse:) withObject:self waitUntilDone:YES];
But some time my main thread method not calling.
i used
dispatch_sync(dispatch_get_main_queue(),^{[self getmostpopularResponse:mostPopularList];});
But this is also have the same problem some time its calling method or some time not calling.
Please help me in this.
I would advise you to create a delegate with which you could notify the main thread after the
completion of the detached thread
Also another solution would be to create an NSOperation and NSOperationQueue instead of a new thread. There you can schedule what you want. For me looks easier, though it depends on you.
Here is a link to help you more with NSOperation
https://developer.apple.com/library/mac/#featuredarticles/ManagingConcurrency/_index.html
I will write this really quickly.
#protocol RespondDelegate
- (void)notifyWithRespond:(NSData *)data;
#end
#interface ContactWebServiceOperation:NSOperation
#property (nonatomic, assign) id delegate;
#end
#implementation ContactWebServiceOperation
#synthesize delegate;
// initialize here.
- (id)initWithDelegate:(id)delegate;
{
if ([self = [super init]) {
self.delegate = delegate;
}
return self;
}
- (void)main
{
if (self.isCancelled) return;
if (nil != delegate) {
// Do your work here...
work();
// When finished notify the delegate with the new data.
[delegate notifyWithRespond:your_data_here];
// Or
[delegate performSelectorOnMainThread:#selector(processImageForDownloadOperation:)
withObject:self waitUntilDone:YES];
}
}
#end
// Now on the view that you want to present the received results
// you have to do one thing.
// Let's say that your view is called View1
#interface View1 : UIViewController<RespondDelegate>
// Here put whatever you like.
#end
#implementation View1
// Put here all your code.
- (void)notifyWithRespond:(NSData *)data
{
// Here you will handle your new data and you will update your view.
}
#end
If I understand correct this should work.
Also, you can change the NSData to whatever you like, as long as you perform the appropriate conversions later.
If it doesn't work take a look on the link from Apple, maybe I have some typo or something.
But in general it looks solid.