UITextField subclass throwing weird exception at becomeFirstResponder - ios

I have character boxes made of my UINavigatableTextField. After each input, responder character should succeed to it's successor. Weirdly, during input of first character, if user enters character 'Q' each time different exception like;
EXC_BAD_ACCESS, or
2012-09-04 14:42:42.600 Kelime Oyunu[6350:707] -[WebScriptObjectPrivate isForShortcutConversion]: unrecognized selector sent to instance 0x21b870
2012-09-04 14:42:42.606 Kelime Oyunu[6350:707] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[WebScriptObjectPrivate isForShortcutConversion]: unrecognized selector sent to instance 0x21b870'
is thrown. My code receiving exception below, the [next becomeFirstResponder] line
- (void) moveToNextCharacter: (MBNavigatableTextField *) character
{
dispatch_async(dispatch_get_current_queue(),
^{
UIControl *next = [character nextField];
if(next == nil)
{
[character endEditing:YES];
}
else if ([next isKindOfClass:[UIButton class]])
{
[next sendActionsForControlEvents: UIControlEventTouchUpInside];
}
else
{
[next becomeFirstResponder];
}
});
}
How can i solve the problem? Thanks in advance.
Edit 1: Found a zombie [next resignFirstResponder] (Thanks to #PhillipMills)
Edit 2: It turns out my problem caused from
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Something done here...
// code block of evil zombie summoner
[textField setText:#"Some text"];
// Something else done here
}
Profile->Instruments->Zombies stating that during setText: some object is released (which is previous string i guess).
I am still unable to solve the problem. My goal is updating textField above with new user input (replacementString:).

All messaging to UIKit objects has to be done on the main queue. While that may be the case, this statement:
dispatch_async(dispatch_get_current_queue()
is troublesome since we don't know what queue you get the message on. Change it to:
dispatch_async(dispatch_get_main_queue()
so its absolutely clear.
Also, under this line:
UIControl *next = [character nextField];
add these statements:
NSLog(#"Character CLASS %#", NSStringFromClass[character class]);
NSLog(#"next CLASS %#", NSStringFromClass[next class]);
Make sure these are what you think they should be.
EDIT: Regarding the use of "unsafe_unretained": if a IBOutlet is contained within "self.view" - that is the primary view - then you should be using weak on iOS - so it gets nil'd for you when the view goes away.
Regarding 'shouldChangeCharactersInRange', if you are simply looking at the characters, you return YES. If you change the text itself - by writing to the field/view directly, then return NO. If you modify text and then return YES, you're in essence changing data behind the control's back with who knows what consequences.

Related

Is it possible to intercept all method class on any UIViewController subclass

Let's say I wanted to be able to intercept any method calls to a UIViewController subclass.
First of all, I swizzle the +(instancetype)alloc method and I check if the current instance isKindOfClass:[UIViewController class]. If it is I go ahead and instantiate my proxy with the target.
///swizzled Alloc
+ (instancetype)monitoredAlloc {
id obj = [self monitoredAlloc];
if([obj isKindOfClass:[UIViewController class]]) {
id proxy = [PMGProxy proxyWithObject:obj];
return proxy;
}
return [self monitoredAlloc];
}
---------------------------------------
/// Proxy class
#implementation PMGProxy
+ (instancetype)proxyWithObject:(id)obj {
PMGProxy *proxy = [self alloc];
proxy.obj = obj;
return proxy;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation setTarget:_obj];
[invocation invoke];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.obj methodSignatureForSelector:sel];
}
- (Class)class {
return [self.obj class];
}
The problem is that I get crashes, so I would expect the implementation of my Proxy is wrong... What am I doing wrong?
Here is the exception:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:'
From the error it seems that the coder is only happy to accept a different class returned from initCoder: rather than earlier in the process at the alloc stage.
Might be worth looking up NSSecureCoding for more detail on the whole process.
While you’re at it, take a look at the stack track that resulted in your exception, it will give you a bit more perspective on just how deep this rabbit hole goes.

Crash while de-registration for KVO

I am seeing some random crashes with my app (although not reproducible when I run through same steps). I am observing the contentOffset property of the scrollview to take some action when it changes.
But I am getting below exception (randomly) with my below code of KVO registration and de-registration.
Is there any safe check that can be applied here.
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <MyPagingController 0x1f05e460> for the key path "contentOffset" from <UIScrollView 0x1f0a8fd0> because it is not registered as an observer.'
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.scrollView addObserver:self forKeyPath:#"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
- (void)viewWillDisappear:(BOOL)iAnimated {
[super viewWillDisappear:iAnimated];
[self.scrollView removeObserver:self forKeyPath:#"contentOffset"];
}
Your unsubscribing code somehow gets hit more often than the subscribing code. Unfortunately KVO does not handle this nicely and the failure throws an exception rather than just doing nothing as you would expect. You either need to make sure it only gets hit once, or at least catch the exception like this:
#try
{
[self.scrollView removeObserver:self forKeyPath:#"contentOffset"];
}
#catch (NSException * __unused exception) {}
}
As much as I love KVO, this need to balance observing and removing is a real issue. You will also get crashes if you nil an object that was KVO observing without telling the sending class to de-register you and the class then tries to send an update to the receiver object. Oops! Bad Access!
As there is no official way to ping to see if you have added an observer (which is ridiculous given that a huge part of what goes on in OS is based on KVO), I just use a simple flag system to ensure that nothing gets added more than once. In the example below, I have a bool flag to track the observer status.
#interface RepTableViewController ()
#property (nonatomic, assign) BOOL KVOSet;
#end
#pragma mark - KVOObserving
- (void)_addKVOObserving
{
//1. If the BOOL flag shows we are already observing, do nothing.
if (self.KVOSet) return;
//2. Set the flag to YES BEFORE setting the observer as there's no guarantee it will happen immediately.
self.KVOSet = YES;
//3. Tell your class to add you up for the object / property you want to observe.
[[ELRepresentativeManager sharedManager]addObserver:self
forKeyPath:#"myRepresentative"
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)_removeKVOObserving
{
//1. Do nothing if we have not set an observer
if (!self.KVOSet) return;
//2. If we have an observer, set the BOOL flag to NO before removing
self.KVOSet = NO;
//3. Remove the observer
[[ELRepresentativeManager sharedManager] removeObserver:self
forKeyPath:#"myRepresentative" context:nil];
}
Yes, using flags to check state is frowned upon by the coding-police. But there really is no other way to be sure except to bean count.
Whatever you do, remember that some classes need to observe even after viewWillDisappear is called (but the view still exists somewhere in the hierarchy), so you can't just do the observe/remove from viewWillAppear/WillDisappear trick. You may need to use delegate callbacks to the object that created the view (and will destroy it) to ensure you never leave a KVO calling class hanging. Having said that, I'm no expert in this stuff, so I'm sure there are people who can do this with way more finesse than my patented bean-counting lol

iOS Switch statement not working?

I've got a View Controller with five buttons. Each button should trigger a modal segue to a different view controller based on the tag of the button, represented by declared constants:
- (IBAction)aButtonTapped:(UIButton *)sender
{
[self buttonForSegue:sender];
}
-(void) buttonForSegue:(UIButton *)sender
{
switch ([sender tag])
{
case aVsAButton_tag:
[self performSegueWithIdentifier:#"aVsAModal" sender:self];
break;
case cVsCButton_tag:
[self performSegueWithIdentifier:#"cVsCModal" sender:self];
break;
case actVsAllButton_tag:
[self performSegueWithIdentifier:#"actVsAllModal" sender:self];
break;
case catVsAllButton_tag:
[self performSegueWithIdentifier:#"catVsAllModal" sender:self];
break;
case customDatePickerButton_tag:
[self performSegueWithIdentifier:#"customDatePickerModal" sender:self];
break;
default:
break;
}
}
Regardless of which button I hit, the app crashes in the sim with this message (only the button names change):
2014-02-10 19:11:47.305 WMDGx[24366:a0b] -[ReportViewController aVsAllButton:]: unrecognized selector sent to instance 0x8a911e0
2014-02-10 19:11:47.307 WMDGx[24366:a0b] * ** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ReportViewController aVsAllButton:]: unrecognized selector sent to instance 0x8a911e0'
My code looks reasonable to me, but apparently not. Can someone please tell me where I'm going wrong?
Thanks!
The error you're reporting would not be caused by the code you posted. I'm guessing that you have an IBAction link in your buttons and that action method no longer exists. Open your storyboard file, select the scene for your view controller, and use the connections inspector to check the action link for each button. You may need to break the connection and re-link it to the proper IBAction.
Looks like this has nothing to do with your switch statement, but rather calling a function that doesn't exist on one of your controllers.
Which line of code does it fail on? Entering the buttonForSegue? At the start of the switch? during one of the performSegueWithIdentifier?
Things you can try:
Ensure that sender is actually a UIButton class
Ensure that the tag is set on the button
Check the view controller that you are seguing to implements the method aVsAllButton

Returning an enum ivar causes EXC_BAD_ACCESS

I'm writing a game where a MainGameplay class keeps track of whose turn it is by setting an ivar to an enum value:
typedef enum {
GAME_NOT_STARTED,
PLAYER_1_TO_MOVE,
PLAYER_2_TO_MOVE
} WhoseTurnIsIt;
Then the GameBoard class checks whether a move attempt is valid, and calls either turnEnded:(WhoseTurnIsIt)turn or reportTurnFailure:(WhoseTurnIsIt)turn in MainGameplay.m.
I get EXC_BAD_ACCESS as soon as I try to access this returned value back in MainGameplay, in the receiving methods. Seems like I should retain something, but you can't retain an enum. In the debugger the values are there, so I don't understand what's being accessed improperly.
This is the code doing the calling in GameBoard:
-(void)buttonPressed:(id)sender {
CCArray *kids = [[[CCDirector sharedDirector] runningScene] children];
if (!mainScene) { // mainScene is an ivar on each the GameBoard's buttons.
for (CCScene *s in kids) {
// this looks crazy because the "main" scene is actually a controlling layer that has as a child the main gameplay layer:
if ([s isKindOfClass:[ControlLayer class]]) {
self->mainScene = (MainGameplay *)((ControlLayer *) s).gameLayer;
}
}
}
if (MOVE_NO_ERROR == [self checkMove:mainScene.turn]) {
[self setMove:mainScene.turn];
[mainScene turnEnded:mainScene.turn]; // This line and the next are the ones causing the EXC_BAD_ACCESS
} else [mainScene reportTurnFailure:mainScene.turn]; // This line too.
}
EDIT Functions in mainScene being called go like this:
-(void) reportTurnFailure:(WhoseTurnIsIt)_turn {
NSLog(#"MainScene still valid"); // This line works fine
NSLog(#"Bzzzzzt. Player %#, try again", _turn); // This line crashes BUT _turn shows up with a proper value in the debugger.
}
_turn is not an object, but the %# format specifier says that the argument is an object. Use %i instead.
-(void) reportTurnFailure:(WhoseTurnIsIt)_turn {
NSLog(#"MainScene still valid"); // This line works fine
NSLog(#"Bzzzzzt. Player %i, try again", _turn); // This line crashes BUT _turn shows up with a proper value in the debugger.
}

iOS - removeFromSuperview crashes the app sometimes

I have a page with a back button that takes me to an inbox. I do this in viewWillDisappear -
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[inboxToolbar removeFromSuperview ];
[inboxToolbar release];
}
If I do this a few times, the app crashes and the exception thrown is "unrecognized selector sent to instance". This happens very intermittently. Any thoughts on how to go about fixing / debugging it?
Assuming the crash is accessing your inboxToolbar ivar, change your code to the following:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[inboxToolbar removeFromSuperview ];
[inboxToolbar release];
inboxToolbar = nil;
}
This will ensure inboxToolbar does not keep a pointer to a deallocated object. You could also have other issues, would need to see the crash log and more of your code to be sure.
add an exception break point to see where it is being thrown, you probably are sending a message to an object that isn't of the class that you believe it to be, after you have stopped on the exception breakpoint, you can hover over the variable to see what classes they belong to.
if that doesn't help post the actual message and perhaps we can help more, could be iOS version issue, etc.
Try checking whether it will respond to your method first...
if ([inboxToolbar respondsToSelector:#selector(removeFromSuperview)]) {
[inboxToolbar removeFromSuperview];
}

Resources