Why is locationManager:didUpdateHeading: not able to update this global? - objective-c

I want to store the continuously updated values returned by the locationManager:didUpdateHeading: method in either a global int or a property int, so that other functions of the MotionHandler class can use it. However, this delegate method doesn't seem to be able to store its values globally but only locally. Why is that? Is it because it's not an actual MotionHandler method? How can I work around this problem? Thank you for your help.
MotionHandler.m
#import "MotionHandler.h"
#interface MotionHandler()
{
CLLocationManager *locationManager;
int degrees; // the global in question..
}
#end
#implementation MotionHandler
-(void) startCompassUpdates
{
locationManager =[[CLLocationManager alloc] init];
locationManager.delegate=self;
[locationManager startUpdatingHeading];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
// This is working, a new value is stored in "degrees" & logged on the console after each update. However it only seems to be updating "degrees" locally..
degrees = (int)locationManager.heading.magneticHeading;
NSLog(#"from delegate method: %i", degrees);
}
-(int) showDegrees
{
return degrees; // This is not working. Whenever I call this method, "degrees" is always zero. Why isn't this global being updated by the previous method ?
}
TheViewController.m
//...
- (void)viewDidLoad
{
[super viewDidLoad];
currentMotionHandler = [[MotionHandler alloc] init];
[currentMotionHandler startCompassUpdates];
while(1==1)
{
NSLog(#"from showDegrees method: %i",[currentMotionHandler showDegrees]); // this just keeps returning zero..
}
}
//...

As per OP request, I have transferred my comments to an answer:
You need to stop using while loop to get constant feedback of the changing value. As Cocoa Touch is an event-based system you can't hijack its run loop by creating an infinite loop in this way. Even outside of an event-based system, using such a tight loop would hurt performance and give little gain.
If you want continuous update (or something that appears to be continuous) you can:
Use a timer to call a method every X milliseconds (see Apple Guide).
Use a background thread (see Apple Guide).
I would prefer to use the timer approach as that has the lowest overhead and runs the method in the same thread as the rest of the UI, avoiding any possible threading issue.

Related

Bad Access Parent is Null - Wht is this Happening?

I understand what the error is, but in this case not what is causing it. In general use it occurs maybe 1% of the time (probably less) but I have found an extreme way to cause it which I will describe below. First, I am using an in-app purchase process I found on Ray Wenderlich's site. Below are the specific pieces of concern here:
.h:
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#interface IAPHelper : NSObject
- (void)requestProductsWithCompletionHandler:RequestProductsCompletionHandler)completionHandler;
#end
.m
#implementation IAPHelper
{
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts); // here is where bad access occurs
_completionHandler = nil;
}
Again, 99%+ of the time this works just fine. Given how infrequent the bad access happens in regular use and it has been difficult to diagnose. However, I found an extreme way to cause the issue. The setup is "Tab 1" is a table view controller and "Tab 2" is a table view controller that uses the code from above. If I quickly switch back and forth between the two tabs I can usually cause the problem to occur anywhere from a few seconds into it to 20-30 seconds. Doesn't happen every time in this scenario but it does the vast majority. As marked above the following line gets a bad access error with Parent is Null.
_completionHandler(YES, skProducts);
To solve the issue I simple do the following:
if (_completionHandler)
{
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
While that fix does work and does solve the problem I am still bothered by why this is occurring. Any thoughts as to the cause of this?
Update:
Apologies to all as I did forget to include the following in what I pasted above.
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
You need to treat your completion block as any other object when you are storing it. So if you are storing your block as a variable and then using it within a different scope from where you assign it, you need to increment the reference count by either copying it or retaining it. The simple solution is to create a strong property to store your block.
Depending on unseen bits of code, your completion handler block might not be being assigned correctly. You need to copy a block if you intend to use it outside of the scope in which it was created.
In your interface, declare your completion handler's storage attribute as "copy".
#property (nonatomic, readwrite, copy) void (^completionHandler)(BOOL, NSArray *);
If you want to control the local variable, you can synthesize the property manually in your implementation:
#synthesize completionHandler = _completionHandler;

awakeFromInsert called twice with nested contexts

This project uses Mogenerator and Magical Record. I have tracked down a bug to the fact that awakeFromInsert is getting called twice. Once for each of my contexts I presume. This is an issue because I need to listen for NSNotifications on this NSManagedObject like this:
- (void)awakeFromInsert
{
// Listen for a return from background mode
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enteringForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
But awakeFromInsert get's called twice which is rather annoying. I want to call a method ONCE when my NSManagedObject is first created.
After searching this solution seems to make a lot of sense. However I can't see how I can add a category onto NSManagedObject when using Mogenerator and MagicalRecord. Without some complex overriding.
In MagicalRecord MR_createEntity calls
if ([self respondsToSelector:#selector(insertInManagedObjectContext:)])
{
id entity = [self performSelector:#selector(insertInManagedObjectContext:) withObject:context];
return entity;
}
Is there a neater solution to this issue?
Ok well this feels very hacky but appears to work. I created the following class methods on my human readable NSManagedObject class:
+ (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ {
JWBoard *newobject = [super insertInManagedObjectContext:moc_];
[JWBoard awakeFromCreate:newobject];
return newobject;
}
+ (void)awakeFromCreate:(JWBoard *)board
{
// do setup stuff & add observers
}
Open to much better solutions!
Open to much better solutions!
I wish! Would have been easy enough for apple to not invoke awakeFromInsert, or to at least provide a flag that's true in the context of a "parentProcessSaveRequest." If you look at the call stack for the not-first calls to awakeFromInsert, the stack always contains parentProcessSaveRequest.
Here's some terrible code proving as much:
- (void) awakeFromInsert
{
[super awakeFromInsert];
NSArray* stackArray = [NSThread callStackSymbols];
for (NSString* method in stackArray)
{
if ([method rangeOfString:#"_parentProcessSaveRequest"].location != NSNotFound)
{
NSLog(#"Parent insert %#",self.objectID);
return;
}
}
NSLog(#"First insert %#",self.objectID);
// Initialize here
}
And the log output -- the objectId stays the same:
2014-05-19 20:53:52.964 myApp[1891:a01f] First insert 0x6000000326c0 <x-coredata:///MyEntity/t496E9B17-E170-4A7C-B7D4-7D8B92433E1C2>
2014-05-19 20:53:53.531 myApp[1891:303] Parent insert 0xdca8000eb <x-coredata://7274869F-4BF3-4B8A-9270-A64E54476AAD/MyEntity/p14122>
2014-05-19 20:53:53.537 myApp[1891:303] Parent insert 0xdca8000eb <x-coredata://7274869F-4BF3-4B8A-9270-A64E54476AAD/MyEntity/p14122>
Seems to work for saving to any of the nested contexts that I have, ugly as it is.
Unfortunately I cant figure any reasonable way to determine whether awakeFromInsert is being called in the context of a parentProcessSaveRequest. Come on, Apple! Give us a flag here.
here is the simplest one:
when parentContext is null, means when this context is saved you can do you custom logic, for example incrementing table number
- (void)awakeFromInsert
{
if (!self.managedObjectContext.parentContext) {
//setting tableNumber
[self willChangeValueForKey:#"number"];
[self setPrimitiveNumber:tableNumber];
[self didChangeValueForKey:#"number"];
}
}

Using stuff from other files in Cocos2d?

I am making a game (obviously) and I noticed that my HelloWorldLayer.m file is getting EXTREMELY cramped. I KNOW that there is a way to run methods from other .m files, I just don't know how. For example, I want to have a Character.h and Character.m file. Can I make it so in the HelloWorldLayer init layer, it just uses everything from the Character files instead of having to declare everything in the HelloWorldLayer? I hope my question makes sense, and any help is appreciated. Thanks!
Here is Character.m:
#implementation Character
#synthesize health,velocity;
-(void)dealloc {
[super dealloc];
}
-(id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect
{
if((self = [super initWithTexture:texture rect:rect]))
{
[self scheduleUpdate];
}
return self;
}
-(void)update:(ccTime)dt {
[self setPosition:ccp(self.position.x,self.position.y)];
self = [CCSprite spriteWithFile:#"nukeboyGreen.gif"];
}
#end
And here's HelloWorldLayer.m (I skimped it down and took out the parts that aren't necessary):
self = [super init];
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
character = [Character spriteWithFile:#"nukeboyGreeen.gif"];
character.position = ccp(winSize.width/2,winSize.height/2);
character.scale = 0.15;
[self addChild:character];
Note that I have a Character declared in HelloWorldLayer.h
This is where object oriented programming comes to the rescue. OOP encourages you to encapsulate variables and functions that is pertinent to an object in that object itself. In your case, you should put the methods that are specific to Character in the Character class, and only get your HelloWorld to trigger those methods.
Examples:
#interface Character : CCSprite {
...
}
- (void)didCollideWith:(Object *)object;
- (void)moveTo:(CGPoint)nextPoint;
- (void)shootArrow:(ckDirection)direction;
- (BOOL)isAlive;
- (int)numberOfLivesRemaining;
...
#end
Then in HelloWorldLayer:
[character moveTo:ccp(100, 200)];
[character shootArrow:kDirectionUp];
if (![character isAlive]) {
[self showGameOver];
}
Not only that your HelloWorldLayer is less cluttered, you can easily understand what your code does by simply looking at the reasonably named methods.
EDIT:
To answer your question as in the comment about how to designate the sprite image in Character class:
#implementation Character
- (id)init {
self = [super initWithFile:#"sprite_character.png"];
if (self) {
// further customization
}
return self;
}
#end
EDIT (after code was added to the question):
First let me point out a few mistake (sorry for the lack of softer word):
You rarely need your sprite to call the [self scheduleUpdate] or [self schedule:SEL]. Normally people implement the update (or tick) method at the CCLayer or CCScene level, where the purpose is to check all the actors (sprites, menus, nested layers etc) for collision/interaction and update their attributes. If you just want to animate movement of a sprite to a specific position, just call runAction method from CCLayer (in the init, update, ccTouchBegan or somewhere). You can read cocos2d-iphone tutorial on Actions by clicking here. So, move the update method and the scheduleUpdate call into your HelloWorldLayer, and then you no longer need to override initWithTexture.
I'm seeing you instantiating a CCSprite in the update method. My above point on the inappropriateness of update method in CCSprite notwithstanding, there is something more important you need to understand when you implement a method: that is you need to decide how and how often your method is going to be used/called. Since the update method is going to be called once per frame (that is 60 times per second), it is simply wrong to unconditionally instantiate an object in that method. You are making the iPhone to allocate (and deallocate) the object with no apparent reason, wasting the processor time/power the device has. You might want to ask where should you instantiate the CCSprite. The answer is in the init method because that method is only called once per object instance. Again, all you need to know is whether a method is going to be called once or multiple times, and decide whether a piece of code should be in there or somewhere else.
In your code for HelloWorldLayer, did you realize that you are calling the super init* methods twice. You don't need to call [super init] since [super initWithColor:ccc4( ... )] is going to call specific init method internally. Although it is not entirely wrong to do so, you are going to break the 'assumption' that init is going to be called once per instance, so you might end up breaking some object integrity unintentionally (and believe me it's going to be hard to debug later)
And finally, care to enlighten me what's the real purpose of the line [self setPosition:ccp(self.position.x,self.position.y)];. You basically set the position of the self object to its current position, so that's like saying "hey you, move your position to your current position" and he'll be like "huh?" :P

GUI Update Synch Issues

I am writing a program that displays to a console-like UITextView different events generated by my AudioSession and AudioQueues. For instance, when I detect that my audio route has changed, I just want a quickie message displayed on the screen on my iPhone that this happened. Unfortunately, I believe I am getting into some race condition nastiness, and I'm not sure what the best solution to solve this is.
When I run my program, my debug console spits this out:
bool _WebTryThreadLock(bool), 0x1349a0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
This happens on a line of code:
textView.text = string;
I tried this:
[textView performSelectorOnMainThread:#selector(setText:) withObject:string waitForDone:YES];
And this seemed to have fixed it, but I'm pretty sure I shouldn't be doing something like this to get it to work. Unfortunately, this doesn't work with [UITextView scrollVisibleWithRange:] since this takes an NSRange, which isn't a descendant of NSObject. I think what I am doing is fundamentally wrong.
This code is called from an interruption listener, which runs from the audio queue's thread. Is there something that I should be doing that will make my updates to my textview blocking so I'm not getting this problem?
Thanks.
You are allowed to do anything about the view only from main thread, you did the right thing.
If it requires more parameters or primitive you may need a proxy function.
This is how I make a proxy function
// the caller should be like this
UTMainThreadOperationTextViewScroll *opr = [[UTMainThreadOperationTextViewScroll alloc] init];
opr.textView = textView;
opr.range = NSMakeRange(5, 10);
[UTMainThread performOperationInMainThread:opr];
[opr release];
// the Utility classes goes below
#interface UTMainThreadOperation : NSObject
- (void)executeOperation;
#end
#implementation UTMainThread
+ (void)performOperationInMainThread:(UTMainThreadOperation *)operaion
{
[operaion performSelectorOnMainThread:#selector(executeOperation) withObject:nil waitUntilDone:NO];
}
#end
#implementation UTMainThreadOperationTextViewScroll
#synthesize textView;
#synthesize range;
- (void)dealloc { /* I'm too lazy to post it here */ }
- (void)executeOperation
{
[textView scrollVisibleWithRange:range];
}
#end
PS. some declarations omitted

event scope

Given
#interface Canvas:NSView {
NSNumber * currentToolType;
...
}
declared in my .h file
and in the .m file
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
currentToolType=[[NSNumber alloc]initWithInt:1];
}
return self;
}
and further down
-(void)mouseUp:(NSEvent *)event
{
NSLog(#"tool value in event: %d",[currentToolType intValue]);
//rest of code
}
-(NSBezzierPath *)drawPath:(NSRect)aRect
{
NSLog(#"tool value in draw: %d",[currentToolType intValue]);
//rest of drawPath method code that uses the value of currentToolType in a switch statment
}
-(IBAction)selectToolOne:(id)sender
{
[currentToolType release];
[currentToolType = [[NSNumber alloc]initWithInt:0];
}
-(IBAction)selectToolTwo:(id)sender
{
[currentToolType release];
[currentToolType = [[NSNumber alloc]initWithInt:1];
}
The action methods are the only place where currentToolType is changed. But, for some reason, it seems to be a different instance of currentToolType in the mouseUp. I did not write (or synthesize) accessors for the var as it is used only by itself. I noticed that initWithFrame is called twice - I'm assuming it's for the parent window and the NSView?
What am I missing?THANKS!
This is an XCode generated Document based app using COCOA and Obj-C. I'm new at both.
You mention that initWithFrame: is called twice. Your initWithFrame: should only be called once (unless you happen to have two Canvas views).
Is it possible you have the Canvas view in your nib/xib file and are also creating another in code (with alloc/initWithFrame:)?
In which case you have two Canvas objects. You probably have one hooked up to your controls and the other one is in the window (and thus responding to the mouseUp: and it is giving you the same value every time).
If you have the Canvas view setup in IB, you can fix this problem by removing your code that is creating the second one.
You've probably run in to a special case: NSNumber could have cached instances to represent commonly-used numbers.
Two observations, though:
You're wasting a whole lot of memory using NSNumber when you could be simply using NSIntegers or maybe an old-fashioned enumerated type, completely avoiding the object overhead.
You never actually showed your code for when you look at the instances of NSNumber; without it, there's not really enough information here to answer your question.