Strange Debugger Issue with Local Variables
Update: tested against the latest XCode 4.2 build and it is still the case. So I reported a bug:
Update: after I wrote and published this entry I realized that what I suspected might be right. So I made a breakpoint inside the constructor of the class in question: ProgramStepEntity and later when it is used as local variable. Now all the variable show up in the constructor (actual init method) as well as when it is used as local variable. Now I don’t want to have breakpoints in every constructor just to make sure that the debugger is working correctly. Need to check if that is still the same problem in 4.2 and if yes then I’ll fill a bug report.
Since a while I am bogged with a stupid debugger issue using XCode 4.0.2. So what gives:
In XCode4 you don’t have to declare your variables anymore if you declare them as properties (@property …). The compiler will take the info from the property definition and declare the variable. That works fine and the properties will show in the debugger when they are inside self or a member variable of self. So far so good.
But when I use the same class as a local variable the debugger is not showing that member. This is the code:
@interface ProgramStepEntity : BaseKVCMapper {
}
@property (nonatomic, retain) NSString* instruction;
@property (nonatomic, retain) NSArray* parameters;
@property (nonatomic, retain) NSString* target;
Here is another break point where the variable show up:
This can be fixed if I declare the variable:
@interface ProgramStepEntity : BaseKVCMapper {
NSString* instruction;
}
@property (nonatomic, retain) NSString* instruction;
@property (nonatomic, retain) NSArray* parameters;
@property (nonatomic, retain) NSString* target;
Now sure what is wrong but I sometimes see all the variable will show up and sometimes they don’t. It could be that the debugger is storing the list of variable once and if at that time the list is incomplete it will not show the rest.
– Andy
iOS, Blocking User Input and CFRunLoopRun()
Yesterday I worked on new a Lab for my upcoming iPhone course and the topic was how to prevent user input while the application is gathering data from a server. Normally that is not a big issue when you just obtain data but that can be a big issue when you send data because then it could corrupt the server side.
So far I just created an UIView that was semi-transparent on top of the entire view preventing the user to click on anything below. But that did not work properly in every case because as it turned out the user input is not consumed but queued and handled as soon as the main thread becomes available. Therefore if I don’t disable the button or alike the code is executed again. This is the original code:
self.connection = [[[NSURLConnection alloc]
initWithRequest:request delegate:self] autorelease];
// Now show an animation
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIView *window = [[UIApplication sharedApplication] keyWindow];
UIView *shield = [[UIView alloc] initWithFrame:window.bounds];
shield.backgroundColor = [UIColor blackColor];
shield.alpha = 0.5f;
[window addSubview:shield];
spinner.center = shield.center;
[shield addSubview:spinner];
spinner.hidden = NO;
[spinner startAnimating];
// Block the further execution until all data is received
CFRunLoopRun();
[spinner stopAnimating];
[spinner removeFromSuperview];
[spinner release];
[shield removeFromSuperview];
[shield release];
Soon I got the feeling that I block the main UI thread and so the input is not consumed until the system can execute it. After looking for a good solution I was more or less told that I should not use CFRunLoopRun() but let the main thread run its course and deal with the result when it comes in.
That said the transition was not easy but eventually I got all the callbacks in place and the thing worked fine except that the call was to short to test is properly. Thus I added a sleep inside the callback to give myself time to click multiple times but that did not solve the issue. Through debugging I then saw that when data are returned back from the server that it is still done in the main thread and so preventing the view to consume the user input.
This is the code afterwards:
// 1. When the call comes in and we start the loading
self.spinner = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.spinner release];
UIView *window = [[UIApplication sharedApplication] keyWindow];
self.shield = [[UIView alloc] initWithFrame:window.bounds];
[self.shield release];
shield.backgroundColor = [UIColor blackColor];
shield.alpha = 0.5f;
[window addSubview:shield];
spinner.center = shield.center;
[shield addSubview:spinner];
spinner.hidden = NO;
[spinner startAnimating];
// Now execute the call to the web service
self.connection = [[[NSURLConnection alloc]
initWithRequest:request delegate:self] autorelease];
// 2. When the all the data is received we do:
[spinner stopAnimating];
[spinner removeFromSuperview];
[shield removeFromSuperview];
// Call back to the service that we received the data
[self.depotService handleResult:root];
Finally I went to the backend web service and made sure that handling of the call is delayed. Then I was able to test it and voila it worked like a charm. I could click multiple times on the button and when the shielding view went away the user input was consumed and did not cause more calls to the server.
There is still some stuff I have to learn how things are done in iOS in contrast to backend Java as I am used to. On the other hand it forces me to understand the underlying concept in details which will be a great asset when given the class.
– Andy
XCode4, iPhone Class and iOS Recipies
This week was pretty exciting even though I am not going to buy an iPad2 or join into the frenzies.
First Apple release XCode4 which is really great news because now I can develop iPhone Apps on it without having to worry about issues or that it is not fully supported by Apple. This also makes it possible to talk about it publicly and I can use screenshots to discuss issues.
Then I started to work on my iPhone class that I intend to give this year if I find people willing to listen to me. My class is different in many ways but foremost I will use a single example throughout the class. On one hand this is a challenge because there is more to code or to fill in to make it a smooth progress but it also means that I have to develop the class around the labs.
Finally there is another book out there from Matt Drance and Paul Warren from the Pragmatic Bookshelf called iOS Recipes. From the few pages I read so far this books is quite nice and gives beginners and intermediate iOS developers a way to see how other developers do it.
– Andy
First iOS App Submitted
Finally after 4 long weeks with long hours the iPhone App I was coding is submitted for reviewing on the App Store.
At the very end we not only had to fix last minute issues which are always very dangerous but I also ran into an issue where I could not build and archive my app and then symbolicate a crash log afterwards. This was very bad considering the fact that when users would send in Crash Logs we would have no way to figure out what went wrong. Eventually I had a hunch that having XCode 3 and 4 installed could be the culprit. So I went ahead and installed XCode 3.2.5 onto a Mac that had not prior installation and after resolving all the issues like missing Certificates I was finally able to create an archive and symbolicate a crash log.
Now for the App it means we have to wait for Apple’s verdict but for me it means that I start on another project today but in contrast that is a project with normal hours and no hard deadline.
-Andy
Adding Frameworks to XCode4
Every time I need to add a new Framework to an iPhone application on XCode4 I am dumbfounded because I don’t see how it is done. Well, I did figure it out again and so I want to make a mental note here that I don’t forget. This is done against the latest XCode4 (preview 6) and so your milage might vary.
These are the steps to accomplish that:
- Select the Project on the left hand side bar with the blue icon on the left
- Inside there select the target (left side bar inside the editor pane or just right to the project tree where we selected the project)
- Click on the Build Phases tab on the top
- Open the Link Binary with Libraries bar
- Click on the + button to add a new library
- Select the desired Framework in the pop-up dialog
- Now just below the project icon on the left you will see the Framework appear (with the yellow box icon)
- Drag the Framework onto the Frameworks Group icon (yellow folder icon) so that it is listed inside that group
Build and Enjoy.
-Andy
Object-C / iPhone and Switch Statements
Lately (meaning the last year or so) I started to be a lousy blogger. Either I have nothing to say or I am so busy that I don’t have time for it. So this time where I started to develop and iPhone app for hire with a hard deadline leaving a lot of work to be done. So far the project progresses well and there is little head banging. Still I ran into an old strange issue with Object-C and wasted a few hours. Eventually I dawned on my that I had this issue beforehand and then it was fixed right away.
So what happened? I wanted to create an enum to store a flag of what the program should do next like display the row, display the row’s detail or query the server for data. That looked like this:
enum Actions {
row,
detail,
search
};
typedef enum Actions Actions;
Then I wanted to use it in a switch-case statement to execute the appropriate action:
switch( actions ) {
case search:
NSString *query = (NSString *) action.data;
return;
case detail:
childViewController = [[TestViewController alloc]
initWithNibName:@"TestDetail"
bundle:nil];
break;
default:
NSDictionary *venue = (NSDictionary *) action.data;
childViewController = [[Test2TableViewController alloc]
initWithRows:rows];
}
But then I get this error: Expected expression before NSString which looks wrong. I am not sure why but the fix is simple. One just needs to wrap each block between case ???: and break into a {} block. That’s it:
switch( actions ) {
case search:
{
NSString *query = (NSString *) action.data;
return;
}
case detail:
{
childViewController = [[TestViewController alloc]
initWithNibName:@"TestDetail"
bundle:nil];
}
break;
default:
{
NSDictionary *venue = (NSDictionary *) action.data;
childViewController = [[Test2TableViewController alloc]
initWithRows:rows];
}
}
Hope that helps - Andy