Wednesday, March 30, 2011

"Instituto Velasco" application now on App Store

Hi,

My application "Instituto Velasco" v.1.1 is now released on App Store. Click here to go to app details.

Thanks

Saturday, February 26, 2011

NSZombieEnabled = YES: One little hint that may help you a lot!

Hi friends,

Recently, I've been stuck on a crash in one of my iOS applications and I hadn't known why it was happening. I had opened Xcode Console for clues, but the application simply crashed without any apparent error messages or warnings.

I decided to go to Applications > Utilities > Console.app and I saw this information below:


25/02/11 22:44:08 SpringBoard[913] Application 'Velasco' exited abnormally with signal 10: Bus error
25/02/11 22:44:08 ReportCrash[918] Saved crash report for Velasco[915] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-224408_MacBook-de-Marcelo-Palermo.crash

By looking at the crash file, I found this:

Process: Velasco [915]
Path: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/4.2/Applications/1319C807-6DE2-4CD4-9E3E-1CC50A50EC84/Velasco.app/Velasco
Identifier: Velasco
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: launchd [183]

Date/Time: 2011-02-25 22:44:07.777 -0300
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6

Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000008
Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Application Specific Information:
objc_msgSend() selector name: release
iPhone Simulator 235, iPhone OS 4.2 (iPhone/8C134)

Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x0116aa63 objc_msgSend + 23
1 UIKit 0x003aef1d -[UINavigationController setDisappearingViewController:] + 64
2 UIKit 0x003ac4f6 -[UINavigationController _clearLastOperation] + 56
3 UIKit 0x003ace3f -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 852
4 UIKit 0x00539e23 -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] + 345
5 UIKit 0x0053afd2 -[UINavigationTransitionView _cleanupTransition] + 712
6 UIKit 0x00326665 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 294
7 UIKit 0x003264f7 -[UIViewAnimationState animationDidStop:finished:] + 77
8 QuartzCore 0x01eef6cb run_animation_callbacks(double, void*) + 278
9 QuartzCore 0x01eef589 CA::timer_callback(__CFRunLoopTimer*, void*) + 157
10 CoreFoundation 0x00fe9fe3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
11 CoreFoundation 0x00feb594 __CFRunLoopDoTimer + 1220
12 CoreFoundation 0x00f47cc9 __CFRunLoopRun + 1817
13 CoreFoundation 0x00f47240 CFRunLoopRunSpecific + 208
14 CoreFoundation 0x00f47161 CFRunLoopRunInMode + 97
15 GraphicsServices 0x0193d268 GSEventRunModal + 217
16 GraphicsServices 0x0193d32d GSEventRun + 115
17 UIKit 0x0030442e UIApplicationMain + 1160
18 Velasco 0x0000264c main + 102 (main.m:14)
19 Velasco 0x000025dd start + 53


My application is Navigation-based. So, the crash pointed that something was wrong when popping back to RootController. The objc_msgSend indicates much probably I was trying to pass a message to an already released object. But this was not enough to detect where issue happened and more: how to solve the crash.

By researching on iOS, I discovered the NSZombie object that definitely would help me. Enabling it is surprisingly easy:

1) In Xcode, set your building configuration to Debug
2) In Xcode treeview, look for Your_Project_Name > Executables > Your_Executable_Name and right-click it > Get Info
3) Look for the following screen:

4) As shown in above picture (click it to enlarge), within the "Variables to be set in the environment", click on the + button at bottom-left corner and add the following information:

Name: NSZombieEnabled
Value: YES

5) Recompile the application and force/reproduce the crash again
6) After the crash occurs, go back to Applications > Utilities > Console.app
7) Now you will see a more detailed information:

25/02/11 22:50:48 com.apple.launchd.peruser.501[183] (UIKitApplication:com.marcelopalermo.Velasco[0xa040][1010]) Job appears to have crashed: Bus error
25/02/11 22:50:48 SpringBoard[913] Application 'Velasco' exited abnormally with signal 10: Bus error
25/02/11 22:50:48 ReportCrash[1013] Saved crash report for Velasco[1010] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-225048_MacBook-de-Marcelo-Palermo.crash
25/02/11 22:57:35 SpringBoard[913] Reloading application state for 'com.marcelopalermo.Velasco' as its modification date has changed
25/02/11 22:57:35 SpringBoard[913] Reloading and rendering all application icons.
25/02/11 22:57:51 Velasco[1041] *** -[UIProgressView release]: message sent to deallocated instance 0xeb08c70
25/02/11 22:57:52 com.apple.launchd.peruser.501[183] (UIKitApplication:com.marcelopalermo.Velasco[0x24e7][1041]) Job appears to have crashed: Trace/BPT trap
25/02/11 22:57:52 SpringBoard[913] Application 'Velasco' exited abnormally with signal 5: Trace/BPT trap
25/02/11 22:57:52 ReportCrash[1044] Saved crash report for Velasco[1041] version ??? (???) to /Users/marcelo_palermo/Library/Logs/DiagnosticReports/Velasco_2011-02-25-225752_MacBook-de-Marcelo-Palermo.crash

The first hint of the crashed appeared (see the text in bold): An UIProgressView object was sent a release message, but it was already deallocated. Now look at the .crash new report. Here's the information:

Process: Velasco [1041]
Path: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/4.2/Applications/1319C807-6DE2-4CD4-9E3E-1CC50A50EC84/Velasco.app/Velasco
Identifier: Velasco
Version: ??? (???)
Code Type: X86 (Native)
Parent Process: launchd [183]

Date/Time: 2011-02-25 22:57:51.823 -0300
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6

Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000002, 0x0000000000000000
Crashed Thread: 0 Dispatch queue: com.apple.main-thread

Application Specific Information:
iPhone Simulator 235, iPhone OS 4.2 (iPhone/8C134)

Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 CoreFoundation 0x00f7a057 ___forwarding___ + 183
1 CoreFoundation 0x00f79f22 _CF_forwarding_prep_0 + 50
2 Velasco 0x000153b5 -[BlogDetailView dealloc] + 293 (BlogDetailView.m:710)
3 UIKit 0x003aef1d -[UINavigationController setDisappearingViewController:] + 64
4 UIKit 0x003ac4f6 -[UINavigationController _clearLastOperation] + 56
5 UIKit 0x003ace3f -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 852
6 UIKit 0x00539e23 -[UINavigationTransitionView _notifyDelegateTransitionDidStopWithContext:] + 345
7 UIKit 0x0053afd2 -[UINavigationTransitionView _cleanupTransition] + 712
8 UIKit 0x00326665 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 294
9 UIKit 0x003264f7 -[UIViewAnimationState animationDidStop:finished:] + 77
10 QuartzCore 0x01eef6cb run_animation_callbacks(double, void*) + 278
11 QuartzCore 0x01eef589 CA::timer_callback(__CFRunLoopTimer*, void*) + 157
12 CoreFoundation 0x00fe9fe3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 19
13 CoreFoundation 0x00feb594 __CFRunLoopDoTimer + 1220
14 CoreFoundation 0x00f47cc9 __CFRunLoopRun + 1817
15 CoreFoundation 0x00f47240 CFRunLoopRunSpecific + 208
16 CoreFoundation 0x00f47161 CFRunLoopRunInMode + 97
17 GraphicsServices 0x0193d268 GSEventRunModal + 217
18 GraphicsServices 0x0193d32d GSEventRun + 115
19 UIKit 0x0030442e UIApplicationMain + 1160
20 Velasco 0x000024fc main + 102 (main.m:14)
21 Velasco 0x0000248d start + 53

Now the search is complete: the crash reports pointed the offending code at line 710 of BlogDetailView.m. My code at this line was:

(void)dealloc {

//[blogDetailsWebView setDelegate:nil];

[blogDetailsWebView release];

[activ release];

[storiesBookMarksArray release];

[selfButton release];

[pdfFileName release];

[pgv release]; <---- my offending code here!!!!

[receivedData release];

[super dealloc];

}


I needed to comment [pgv release]; out of my code to make it all work fine. And it made total sense. That's because "pgv" is my UIProgressView. Actually, inside one of my methods I have a UIProgressView inside an UIAlertView. And I had coded the following:

pgv = [[[UIProgressView alloc] initWithFrame:CGRectMake(30.0f, 80.0f, 225.0f, 90.0f)] autorelease] ;

[myAlertView addSubview:pgv];


Since it's set to autorelease, I wanted to use this object till the very end of my method. After that, the pgv object is added to autorelease pool. That means when that method lifecycle ends, the autorelease pool is flushed [1]. The (void) dealloc method is called only when exiting my BogDetailView (which of course happens much after my method where I allocate the pgv). But I simply forgot I was using autorelease for my pgv.

At this point, I was issuing a new [pgv release] to an already released pgv. That causes a crash.

The conclusion is: NSZombieEnabled may really help you in situations like the above one. I really consider it's worth to give a try! I hope this article may help you.

Also, don't forget to consult Apple's Objective-C Memory Management Programming Rules [2].

Best Regards

Marcelo

Reference:
[1] O'REILLY MacDevCenter.com. Memory Management in Objective-C.
[2] APPLE, INC. Memory Management Rules.

Sunday, December 12, 2010

Resolving runtime issues with AirPrint and iOS backward compatibility with weak linking

Hi,

Maybe you are running into the same issue. So, I considered to write this topic. All began when I was testing the new AirPrint iOS 4.2 feature with my existing applications. In my case, Implementing AirPrint feature into your application is pretty simple. You just instantiate the Airprint UIKit objects, based on Apple's sample [1] and you are all set. For instance, to print a webview content (that was my case), just add this method into your View Controller .m code and let iOS 4.2 do the rest for you, I mean, show you the window to choose number os copies, select printer etc:

- (void)printWebPage {

if ([UIPrintInteractionController class]) {

UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController];

if(!controller){

NSLog(@"Couldn't get shared UIPrintInteractionController!");

return;

}

void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =

^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {

if(!completed && error){

NSLog(@"FAILED! due to error in domain %@ with error code %u",

error.domain, error.code);

}

};

if ([UIPrintInfo class]) {

UIPrintInfo *printInfo = [UIPrintInfo printInfo];

printInfo.outputType = UIPrintInfoOutputGeneral;

printInfo.jobName = [Singleton sharedInstance].globalBlogTitle;

printInfo.duplex = UIPrintInfoDuplexLongEdge;

controller.printInfo = printInfo;

controller.showsPageRange = YES;

}

if ([UIViewPrintFormatter class]) {

UIViewPrintFormatter *viewFormatter = [self.blogDetailsWebView viewPrintFormatter];

viewFormatter.startPage = 0;

controller.printFormatter = viewFormatter;

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {

[controller presentFromBarButtonItem:self.navigationItem.rightBarButtonItem animated:YES completionHandler:completionHandler];

} else

[controller presentAnimated:YES completionHandler:completionHandler];

}

}

}



Now, take a look at above code. I need to preview if user has an iOS version smaller than 4.2 and avoid crashes or bogus references in my application. iOS 4.1 and previous don't know what UIPrintInteractionController, UIViewPrintFormatter and UIPrintInfo mean. So, I need to test for these classes existence and instruct my code where to go in this case [2].

When you compile the application, compilation is successful and runs fine in iOS 4.2 as well. But try running it in a previous iOS version device or simulator (e.g: iPad 3.2) and your application simply crashes on start. Look at Run - Console Xcode menu and you will face this unpleasant error:

[Session started at 2010-12-12 08:53:27 -0200.]

dyld: Symbol not found: _OBJC_CLASS_$_UIPrintInfo

Referenced from: /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/3.2/Applications/3ABCAD78-5788-4FFA-8F12-B5AD805E1AC5/MyApp.app/MyApp

Expected in: /Developer/Platforms/iPhoneSimulator.platform/Developer/

SDKs/iPhoneSimulator3.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit

in /Users/marcelo_palermo/Library/Application Support/iPhone Simulator/3.2/Applications/3ABCAD78-5788-4FFA-8F12-B5AD805E1AC5/MyApp.app/MyApp


Something is missing... a Weak Linking to your application is missing. iOS 4.2 introduces the weak linking technique in order to preserve compatibility to older iOS versions [3]. That means, if you introduce Airprint feature into your application, that means old iOS users can continue using the application and won't take advantage of Airprint only. So, that's exactly what we need here.

In a straight-forward way, here's what to do to get rid of above error, according to Apple's document [4]:

  • The base SDK for your Xcode project must be iOS 4.2 or newer. The name for this setting in the build settings editor is SDKROOT (Base SDK).
  • The deployment target for your project must be iOS 3.1 or newer. The name for this setting is MACOSX_DEPLOYMENT_TARGET (Mac OS X Deployment Target).
  • The compiler for your project must be the LLVM-GCC 4.2 compiler or newer, or the LLVM compiler (Clang) 1.5 or newer. The name for this setting is GCC_VERSION (C/C++ Compiler Version).
  • You must ensure that any frameworks not available in your project’s deployment target are weakly linked, rather than required
The latest item above is achieved this way:

- Within your Xcode treevieew, expand Targets > , right-click it and choose Get Info
- In the General tab, you will see something like this:

- Right-click the "Required" text under Type column for each framework and change it all to "Weak"
- Recompile your application and test it on iPhone/iPad devices or simulators with previous iOS versions than 4.2 - now it will all work fine.

Have a nice day!

Reference:

[1] APPLE, Inc. Drawing and Printing Guide for iOS.
[2] APPLE, Inc. SDK Compatibility Guide
[3] APPLE, Inc. What's new in iOS
[4] APPLE, Inc. SDK Compatibility Guide

Friday, April 30, 2010

The iPhone OS Architecture - Part 1 (The very basics)

iPhone OS is really amazing! So, I think it's a good idea for us to start studying how iPhone OS works. iPhone OS applications are mostly based on Objective-C programming, which is part of Cocoa Touch architecture. Cocoa Touch? Below, let's see an introduction overview of this Apple architecture for the iPhone.

When I started programming applications for the iPhone, I particularly found it completely different from everything I already coded within my 15 years of coding experience. At the beginning, I admit it was weird... but after studying and acquiring some experience on that, I really found it amazing.

The curious thing is that it's based on NeXTStep Objective-C, from 1982! It's now ressurrected for the iPhone.

First of all, you use Xcode and Interface Builder in parallel. Cocoa Touch is based on MVC (Model - View - Controller) architecture. I mean, you code the Controller in Objective-C and, almost at the same time, use Interface Builder to create the UIKit visual objects and link them to your visual object events into methods, instance variables (outlets) you coded for the Controller. It's a different concept and that's how it works on Cocoa/Cocoa Touch. We are used to create a visual object, click twice on it, and then code its event. Cocoa works different at this point.

Let's go through the brief overview of iPhone OS... First, let's talk about Mac OS X, which was rewritten and recoded entirely in Cocoa and Objective-C. By the way, according to Apple, the Snow Leopard version (10.6) was completely rewritten from its antecessor Leopard (10.5) and the code in 10.6 was reduced to less than a half of previous 10.5 [1]. For the iPhone OS, Apple introduced almost all same benefits they did for the Snow Leopard and this really allows the iPhone OS to run faster and support complex applications, as well. Here is iPhone OS architecture:



The iPhone OS architecture is divided into four important layers [2]:

  • Core OS: includes OS X Kernel, Power Management, Mach 3.0, Keychain, BSD, Certificates, Sockets, File System, Security and Bonjour
  • Core Services: includes Collections, Core Location, Address Book, Net Services, Networking, Threading, File Access, OS Preferences, SQLite and URL utilities
  • Media: includes Core Audio, JPG/PNG/TIFF engines, OpenAL, PDF engine, Audio Mixing, Quartz (2D), Audio Recording, Core Animation, Video Playback and OpenGL ES
  • Cocoa Touch: includes Multi-Touch Events, Alerts, Multi-Touch Controls, Web Views, Accelerometer, People Picker, View Hierarchy, Image Picker, Localization, Controllers

Based on above architecture, we will code in the Cocoa Touch layer in order to access all other layers. For example, if you want your iPhone application to detect the end user GPS coordinates, you will add the Xcode Core Location Framework into your Xcode project. So, you will have Cocoa Touch layer accessing Core Services layer. And so on.

Bibliography:

[1] APPLE, Inc. Mac OS X: Enhancements and Refinements. http://www.apple.com/macosx/refinements/enhancements-refinements.html#installation

[2] APPLE, Inc. iPhone OS Overview. http://developer.apple.com/iphone/library/referencelibrary/GettingStarted/URL_iPhone_OS_Overview