I'm creating a simple C++ application with cmake on Mac.
There is a main in C++ source code file, that create C++ class. Inside this class I'm allocating objective C object, that adding itself to observers in NSNotificationCenter. And I'm not receiving those notifications.
There is a code:
Notifications.h
class LaunchNotification {
public:
LaunchNotification();
virtual ~LaunchNotification();
void StartNotifications();
void StopNotifications();
private:
void *monitor;
};
Notifications.mm
#interface Monitor : NSObject
-(id) init;
-(void) appLaunchedNotification :(NSNotification *) notification;
-(void) appTerminatedNotification :(NSNotification *) notification;
#end
#implementation Monitor
- (id) init
{
self = [super init];
if (self)
{
count = 0;
NSNotificationCenter *notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[notCenter addObserver : self
selector:#selector(appLaunchedNotification:)
name:NSWorkspaceDidLaunchApplicationNotification
object:nil];
[notCenter addObserver : self
selector:#selector(appTerminatedNotification:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
return self;
}
- (void) appLaunchedNotification : (NSNotification *) notification
{
NSString *path = [[notification userInfo]objectForKey: #"NSApplicationPath"];
}
- (void) appTerminatedNotification : (NSNotification *) notification
{
NSString *path = [[notification userInfo]objectForKey: #"NSApplicationPath"];
}
- (void) dealloc
{
NSNotificationCenter *notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[notCenter removeObserver : self];
[super dealloc];
}
#end
LaunchNotification::LaunchNotification() : monitor(NULL)
{}
LaunchNotification::~LaunchNotification()
{
StopNotifications();
}
void LaunchNotification::StartNotifications()
{
if (NULL == monitor)
{
monitor = [[Monitor alloc] init];
}
}
void LaunchNotification::StopNotifications()
{
if (NULL != monitor)
{
[(id)monitor release];
}
}
You need a run loop because otherwise, NSWorkspace has no mechanism to gain control of your application's thread in order to post notifications.
While the docs say run loops are automatically created, they are not automatically executed. Think about it: how can a thread be simultaneously running your code and running the code in the run loop?
Any tasks that you need to do while you are monitoring the notification centre need to be done in the context of runloop events e.g. on an NSTimer event, or you need a separate thread for that other stuff.
Related
I have two applications which are very similar in their BLE functionality however one scans correctly & discovers peripherals where other does not.
They both report no problems initializing their pointer to CBCentralManager & checking that it's on & functional, but one goes on to discover & be able to connect to a device & the other does not.
In the one that doesn't work the call to scanForPeripheralsWithServices goes off without a hitch but the didDiscoverPeripheral callback is never triggered. The biggest difference that i can think of is that one project uses pure Objective-C & ARC whereas the other uses a mix of Objective-C & C++ and does not use automatic reference counting.
Could this be the source of the problem ? If so, is there a way around it? I have been able to work with CoreBluetooth in the past without ARC but it might have changed without my being able to parse that from the docs.
In the event that it's not ARC, the two calls to scanForPeripheralsWithServices are shown below:
functional
- (int) findBLEPeripherals:(int) timeout
{
NSLog(#"start finding");
if (self.CM.state != CBCentralManagerStatePoweredOn)
{
NSLog(#"CoreBluetooth not correctly initialized !");
NSLog(#"State = %d (%s)\r\n", self.CM.state, [self centralManagerStateToString:self.CM.state]);
return -1;
}
[NSTimer scheduledTimerWithTimeInterval:(float)timeout target:self selector:#selector(scanTimer:) userInfo:nil repeats:NO];
[self.CM scanForPeripheralsWithServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:#RBL_SERVICE_UUID]] options:nil];
NSLog(#"scanForPeripheralsWithServices");
return 0; // Started scanning OK !
}
non-functional
- (void)startScan
{
NSLog(#"startScan");
isScanning = true;
NSDictionary *options = nil;
didUpdateDiscoveredDeviceFlag = [delegate respondsToSelector:#selector(didUpdateDiscoveredRFduino:)];
if (didUpdateDiscoveredDeviceFlag) {
options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
}
[devices removeAllObjects];
if (self.central.state != CBCentralManagerStatePoweredOn)
{
NSLog(#"CoreBluetooth not correctly initialized !");
// NSLog(#"State = %d (%s)\r\n", self.central.state, [self centralManagerStateToString:self.central.state]);
}
[NSTimer scheduledTimerWithTimeInterval:(float)60 target:self selector:#selector(scanTimer:) userInfo:nil repeats:NO];
[self.central scanForPeripheralsWithServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:#RBL_SERVICE_UUID]] options:#{ CBCentralManagerScanOptionAllowDuplicatesKey : #YES }];
if (didUpdateDiscoveredDeviceFlag) {
[self startRangeTimer];
}
}
And the relevant parts of the two header files:
functional
#interface BLE : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate> {
}
#property (nonatomic,assign) id <BLEDelegate> delegate;
#property (strong, nonatomic) NSMutableArray *peripherals;
#property (strong, nonatomic) NSMutableArray *peripheralsRssi;
#property (strong, nonatomic) CBCentralManager *CM;
#property (strong, nonatomic) CBPeripheral *activePeripheral;
non-functional
#interface BLEDeviceManager : NSObject <CBCentralManagerDelegate>
{
}
+ (BLEDeviceManager *)sharedDeviceManager;
#property (strong, nonatomic) CBCentralManager *central;
Any advice much appreciated!
So this is a bit silly but a reboot of the phone + clean/build fixed everything. I suspect that the two applications weren't playing nice with one another in releasing the CBCentralManager handle but that's just a guess. I'm not deeply familiar with CoreBluetooth or with Objective C to be honest so my guesses aren't all that well informed.
In cocos2d v3, I could not find something like CCTargetedAction.
It is required in my project, so I copied code from cocos2d v2.
#interface CCTargetedAction : CCActionInterval
/** This is the target that the action will be forced to run with */
#property(readwrite,nonatomic,retain) id forcedTarget;
#property(readwrite,nonatomic,retain) CCActionFiniteTime* action;
/** Create an action with the specified action and forced target */
+(id)actionWithTarget:(id)target
action:(CCActionFiniteTime*)action;
/** Init an action with the specified action and forced target */
-(id)initWithTarget:(id)target
action:(CCActionFiniteTime*)action;
#end
#implementation CCTargetedAction
+(id)actionWithTarget:(id)target
action:(CCActionFiniteTime*)action
{
return [(CCTargetedAction*)[self alloc] initWithTarget:target
action:action];
}
-(id)initWithTarget:(id)target
action:(CCActionFiniteTime*)action
{
self = [super initWithDuration:action.duration];
if(self)
{
self.forcedTarget = target;
self.action = action;
}
return self;
}
-(id)copyWithZone:(NSZone*)zone
{
CCAction *copy = [(CCTargetedAction*) [[self class] allocWithZone: zone]
initWithTarget:_forcedTarget
action:[_action copy]];
return copy;
}
- (void) startWithTarget:(id)aTarget
{
[super startWithTarget:aTarget];
[_action startWithTarget:_forcedTarget];
}
- (void) stop
{
[_action stop];
}
- (void) update:(CCTime) time
{
[_action update:time];
}
#end
But my CCTargetedAction runs action twice.
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CCActionCallBlock* block = [CCActionCallBlock actionWithBlock:^{
CCLOG(#"call block");
}];
CCTargetedAction* action = [CCTargetedAction actionWithTarget:self
action:block];
[self runAction:action];
}
If I touch the screen once, then the log message is output twice.
2014-04-07 22:09:57.439 TargetedActionTest[3924:60b] call block
2014-04-07 22:09:57.455 TargetedActionTest[3924:60b] call block
Why this code runs action twice?
Thank you.
This problem is solved by overwriting -(BOOL)isDone method.
-(BOOL)isDone
{
return [_action isDone];
}
I was referring to this post.
http://cocos2d-x.org/forums/6/topics/39546
I subclassed CCSpriteBatchNode to make an object that conforms to NSCoding. I was mainly interested in the string name of the CCSpriteBatchNode. After setting break points I realized that the object's string name is always nil. I have a feeling it that my overridden methods might be a contributing factor but I'm not really sure about that. Please see relevant code below:
SpriteBatchNode interface:
#interface SpriteBatchNode: CCSpriteBatchNode {
NSString* batchImageName;
}
SpriteBatchNode implementation:
const NSUInteger defCapacity = 29;
#implementation SpriteBatchNode
#synthesize batchImageName;
+(id)batchNodeWithFile:(NSString*) imageFile
{
return [[self alloc] initWithFile:imageFile capacity:defCapacity];
}
-(id)initWithFile:(NSString *)fileImage {
self = [super initWithFile:fileImage capacity:defCapacity];
if (!self) return nil;
batchImageName = fileImage;
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
NSString* spriteBatchNodeFileImage = [[aDecoder decodeObjectForKey:#"batchImageName"] copy];
self = [super initWithFile:spriteBatchNodeFileImage capacity:defCapacity];
if (!self) return nil;
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:batchImageName forKey:#"batchImageName"];
}
#end
If you aren't using ARC I see two problems here:
batchImageName string is not retained
batchNodeWithFile: is not sending autorelease to the returned instance
Other than that you're using an unusual init style, the common style is this:
if (self)
{
batchImageName = fileImage;
}
return self;
Checking self for nil, then returning nil if it is, is somewhat redundant.
I'm threeaing some tasks like this :
RootViewController
- (void)viewDidLoad {
[NSThread detachNewThreadSelector:#selector(findSomething) toTarget:self withObject:nil];
}
- (void) findSomething {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
doMoreThings
[pool release];
}
- (void) doMoreThings {
doMoreMoreMoreThings on different objects
}
- (void) foundSomething:(NSFoundThing*)foundObj {
do your stuff
}
oneObject
- (void) doMoreMoreMoreThings {
do things
[self performSelectorOnMainThread:#selector(foundSomething:) withObject:thingFound waitUntilDone:NO];
}
gives
-[KMLParser foundSomething:]: unrecognized selector sent to instance 0x5888080
What is the problem ?
The threading is irrelevant. Some of the code you're not showing us is making it so you are sending the foundSomething: selector to an object that doesn't handle that message. Route the message to an object that does handle it, and your problem will go away.
See also "Unrecognized selector sent to instance".
I have a contact listener that handles contact between two box2d bodies. I am accessing the bodies from the Contacter in the HelloWorldLayer since box2d recommends that contacting bodies should be saved and changes implemented after the timestep. Please see the code below:
Contacter.h:
#import "CCPhysicsSprite.h"
#interface Contacter : CCPhysicsSprite {
}
#property(nonatomic, assign) NSMutableArray* arrayOfBodies;
#property(nonatomic, assign) CCPhysicsSprite* spriteToDestroy;
-(void)physicsSpritesContact:(CCPhysicsSprite*)onePhysicsSprite otherSprite:(CCPhysicsSprite*)twoPhysicsSprite;
#end
Contacter.mm:
#import "Contacter.h"
#import "Box2D.h"
#implementation Contacter
#synthesize arrayOfBodies = _arrayOfBodies;
#synthesize spriteToDestroy = _spriteToDestroy;
-(void)destroyBodies:(b2Body*)body {
_arrayOfBodies = [[NSMutableArray alloc] init];
NSValue *bodyValue = [NSValue valueWithPointer:body];
[_arrayOfBodies addObject:bodyValue];
}
-(void)physicsSpritesContact:(CCPhysicsSprite*)onePhysicsSprite otherSprite: (CCPhysicsSprite*)twoPhysicsSprite; {
int firstTag = onePhysicsSprite.tag;
int secondTag = twoPhysicsSprite.tag;
if (((firstTag == 90) && (secondTag == 101 )) || ((firstTag == 101) && (secondTag == 90))) {
if (tag1 == 90) {
[self destroyBodies:onePhysicsSprite.b2Body];// adds body to array to be destroyed
spriteToDestroy = onePhysicsSprite; // taking note of sprite to be destroyed
}
else if (tag2 == 90) {
[self destroyBodies:twoPhysicsSprite.b2Body];
spriteToDestroy = twoPhysicsSprite;
}
}
}
The following method within HelloWorldLayer.mm is called in the update method:
-(void)removeDestroyedBodiesAndSprites {
bodyContact = [Contacter node];
if ([bodyContact arrayOfBodies]) {
for (NSValue* bodyValue in [bodyContact arrayOfBodies]) {
b2Body *removeBody;
removeBody = (b2Body*)[bodyValue pointerValue];
world->DestroyBody(removeBody);
removeBody = NULL;
[self removeChild:[bodyContact spriteToDestroy]];
}
}
}
There is contact but the sprite is not removed and body is not destroyed in removeDestroyedBodiesAndSprites. After testing with a CCLOG I found that the for loop was not satisfied meaning that the arrayOfBodies could be null. Which is surprising since the contact was established. I would appreciate your assistance.
UPDATED
Below is the contact listener:
TestContactListener.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#import "GameObjects.h"
#import "Contacter.h"
class TestContactListener : public b2ContactListener {
public:
Contacter* contacter;
void BeginContact(b2Contact* contact);
};
TestContactListener.mm:
#import "TestContactListener.h"
void TestContactListener:: BeginContact(b2Contact *contact)
{
contacter = [Contacter node];
b2Fixture *fixtureA = contact->GetFixtureA();
b2Fixture *fixtureB = contact->GetFixtureB();
b2Body *fixtureABody = fixtureA->GetBody();
b2Body *fixtureBBody = fixtureB->GetBody();
CCPhysicsSprite* physicsSprite = (CCPhysicsSprite*)fixtureABody->GetUserData();
CCPhysicsSprite* physicsSprite2 = (CCPhysicsSprite*)fixtureBBody->GetUserData();
[contacter physicsSpritesContact:physicsSprite otherSprite:physicsSprite2];
}
Move this from the destroyBodies method to an init method, so it is only called once:
_arrayOfBodies = [[NSMutableArray alloc] init];
Take a good look at your destroyBodies method. You create a new array, replacing any existing array, every time you call it. Therefore you will only destroy the last body you passed in to that method.
If you are not using ARC you'll also leak all the replaced arrays.