Tuesday, April 19, 2011

Beware of the self keyword within setter functions

If you have read my previous post Self keyword and setter functions, you will find it easier to follow along with this post.

In the last post, I asked you to use the self.member syntax when setting a member variable.

In this post, I am going to ask you not to use the self.member syntax within your setter function.

This is applicable only if you are writing your own custom setter function. The following example summarizes it all:

- (void)setMyArray:(NSArray*)tmpArray{

  // First, retain the value
  [tmpArray retain];

  // Next, release the previous value
  [myArray release];


  // Now here is where you need to be careful
  // Set the value
 
// WRONG: Don't use self.member within a setter function 
  // self.myArray = tmpArray


  // RIGHT: Always call the member variable directly within the   setter function
   myArray = tmpArray
}


Now for those of you who are still curious and wondering why you shouldn't use the self.member syntax within the setter function, here is the reason.

Using self.member syntax invokes the setter function of that variable, so if you use the self.member syntax within the setter function the setter function will be called recursively and your program will be in an infinite loop.

Monday, April 18, 2011

Self keyword and setter functions

Let me begin this post with an example:
Consider this variable:

NSArray * myArray;
@property(nonatomic, retain) NSArray * myArray;


The above lines of code causes the compiler to generate a default setter function for the myArray variable which retains the variable.

The compiler generated setter function would like something like:

(void)setMyArray:(NSArray *)tmpMyArray{
  [tmpMyArray retain];
  [myArray release];
  myArray = tmpMyArray;
}


Observe that the setter function releases the current myArray before assigning a new value to it. That is why, when we set a value to a variable we should always ensure that we use the self.member syntax.

// The following line invokes the setter function to set this value
self.myArray = anArray;

// The following line does not invoke the setter function and thus causes a memory leak because release is not called on the previous value of myArray.
myArray = anArray;

Another thing to be remembered is, if we are defining a custom setter function, we MUST implement all the steps that a compiler-generated setter would contain.

Monday, April 11, 2011

Positioning the UIPopoverController after rotation

When creating applications for iPad, one of the most important feature we need to take into account is device orientation change. One issue that I came across while rotating a popover controller was maintaining the position of the popover controller after rotation.

So if you are at the stage where you still have to fight your popover controller to get it to behave, continue reading to know how I did it!

Here, we will consider showing the popover controller from a bar button.

Here's a recap for creating a popover controller:

1. Instantiate your nib file.
    MyNib * myNib = [[MyNib alloc] initWithNibName: @"MyNib" bundle: nil];

2. Get the instance of the bar button item from which the popover controller should be displayed:
    UIBarButtonItem *myBarButton = (UIBarButtonItem *)self.barButton;

3. Instantiate the popover controller and set the delegate
    self.myPopoverController = [[UIPopoverController alloc] initWithContentViewController: myNib];
     self.myPopoverController.delegate = self;


4. Set the size of the popover content controller
    [self.myPopoverController setPopoverContentSize: CGSizeMake(anyWidth, anyHeight)];

5. Finally present the popover
   [self.myPopoverController presentPopoverFromBarButtonItem: myBarButton
                                    permittedArrowDirections: UIPopoverArrowDirectionUp
                                                    animated: YES];


Put the above lines of code into a function called showMyPopover.

Now we come to the interesting part, handling the popover during rotation:

Override the didRotateFromInterfaceOrientation orientation handler like follows:

- (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation) fromInterfaceOrientation {
    if (self.myPopoverController) {
        [self.myPopoverController dismissPopoverAnimated: YES];
        [self.myPopoverController release];
        self.myPopoverController = nil;
    }

    [self showMyPopover];
}


And that's it, each time your orientation changes, your popover rotates appropriately.
If your popover controller contains a UITableViewController and you want to maintain state each time the UIPopoverController is rotated, have a look at my blog How to maintain the UITableView scrolled location

Thursday, April 7, 2011

How to maintain the UITableView scrolled location.

Recently, in one of my code, I discovered an issue in my UITableView's scrolled location. The issue was that, my UITableView was not maintaining its scroll location. My UITableView happens to be in a UIPopoverController and it has so many rows that it won't fit on screen. So it would take around 5 or 6 scrolls to see the entire contents of the UITableView.

So here is the exact problem:
On the main page, I have a name value. It is a superset of these name values that need to be displayed in the  UITableView when I click on a button to display the UITableView as a popover. What I need to do when the popover is displayed, is I need the name displayed on the main page to be shown as selected in the UITableView. Now if the name to be selected is within the first 20 rows in the UITableView then I have no issue, whenever the user clicks on the popover button, the tableview is shown with the required name selected.

However, if the name on my main page is a value that is not within the first 20 rows (maybe it is visible only after the 3rd or 4th scroll), so when I display the UITableView the selected value will not be seen because it is not in the current page. From a user point of view, this is a defect so I needed to correct it to improve the user experience.

This is how I fixed it.
 1. Calculate the saved scroll position. You can do it either by taking the content offset as follows:

CGPoint savedScrollPosition = [tableviewObject contentOffset];

or if this is not possible in some places, you can calculate the offset manually as follows:

int index = [namesArray indexOfObject:category.categoryName];
self.savedScrollPosition = CGPointMake(0, HEIGHT_OF_CELL * index);

2. Set the savedScrollPosition into the UITableViewController class.

3. In the viewDidAppear function of the UITableViewController class, write the following line:

[categories setContentOffset:savedScrollPosition animated:NO];

or you can set animation to yes if you want the animation.

So, eventhough I make a new instance of my UITableViewController each time the popover button is clicked, my users never lose their continuity.

Good trick!

Monday, April 4, 2011

How to add existing frameworks in XCode 4?

In the older version of XCode this was easily done by right-clicking the Frameworks group and selecting "Add existing".

In XCode 4 however, they decided to put it in a less easily accessible location.
So here's how to get there!

1. In Project Navigator, select the project Name.
2. Next, select your project's target.
3. Select the Build Phases tab.
4. Expand the "Link Binaries with Library" tab.
5. Select the "+ button to add an existing framework.

The following image sums it all up.

Adding existing frameworks in xcode 4

Friday, April 1, 2011

Where is the XCode 4 build directory?

In all previous versions, XCode used to create a build directory inside the project folder and the executables were saved in that location.

However, XCode 4 changed all that.
Infact, I spent quite sometime searching for my executables.
XCode 4 uses the derived data location for storing your builds by default.

Just click on the XCode menu on the top bar, and select Preferences -> Locations tab.
Here under build location, you can see the default setting selected as "place build products in derived data location (recommended)".

And if you check the "Derived Data Location" section of this tab you will see the default location is /Users/..../Library/Developer/XCode/DerivedData.
You will see a small arrow next to this path and clicking on it takes you directly to this location and voila there is your build directory!

How to use enums in objective c?

We can use enums to define data types which will take only some specific values.
Often we come across code that has magic numbers in it.

See the code snippet below:
if (choice == 1)

What choice is intended by 1 is unclear over here.
So for this purpose we can use constants with meaningful names.

For instance, suppose the choice equals 1 means "edit Data"
so we can define a constant as follows:
const int kSelectedEditData = 1;

and we could rewrite the code above as follows:
if (choice == kSelectedEditData)

This makes it more readable. Suppose we want to do some processing in a switch statement, this approach would not work as the switch statement does not allow the use of constant values.
So inorder to get over this limitation we define an enum for all the choices and use that enum in the switch.

enum {
      kSelectedEditData = 1,
      kSelectedDeleteData,
      kSelectedDisplayData
    } Choices;


You can now create a variable of this enum type as follows:
enum Choices myChoices;

Notice that we have to use the enum keyword during declaration which is not a very pretty sight. Just create a typedef to get rid of that.
typedef enum Choices Choices;

So now you can declare your enum variable as follows:
Choices myChoices;

Ok, now lets see how we can use this in a switch statement:
switch (myChoices){
   case kSelectedEditData:
      // do something
   break;

   case kSelectedDeleteData:
      // do something
   break;

   case kSelectedDisplayData:
        // do something
   break;
}


There, now everything is much more readable.

The only problem with the enum type is even if you declare a variable of the enum type, the compiler will not give you an error if you set a value other than the enum values to this type.
eg:- myChoices = 10; // no compiler errors

So enums are not type safe in objective c, but you can use them to make meaningful data types.

Tuesday, March 29, 2011

Use the copy attribute with NSString objects

NSStrings should not be retained instead always be sure to copy them so you have two entirely different NSString objects.

What happens if I retain an NSString?

If we retain an NSString value, it can be modified by some other object and the class containing the NSString object will not know.

eg:- NSMutableString * testString = [NSMutableString stringWithString@"Paul"];
        Student * st = [[Student alloc] init];
        st.name = testString;
       [testString setString:@"Jane"];

In the above example, you can see that we created an immutable string and assigned the name "Paul" to it. Then we set this value to the name property of the Student object. Now what we would want is that the student name should remain "Paul" unless changed explicitly. Now observe the next line of code, since the string object we declared was of type NSMutableString we can change the contents of it.

If the Student.name property is marked as retain, then the value of name will be changed to "Jane" even if it is not set explicitly. Whereas if the Student.name property is marked as assign, then the value of name will remain as "Paul" if someone tries to change the value of testString externally.

So to be on the safe side, always mark your NSString objects with the copy property. If the value assigned to it is mutable, then it will get copied otherwise it will be retained. So we will have no issues either way!

Monday, March 28, 2011

Delegates - To assign or to retain?

Well, the right answer is that delegates need to be marked as assign.

What I mean by assign is when you create a property for the delegate it should be as follows:
@property(nonatomic, assign) id delegate;

Why does it matter?

The whole reason behind this is related to retain cycles. The concept is clearly mentioned in the apple docs (see the section 'Retain cycles' in the Apple docs).

There can be two cases here. However, I feel the second case is the one that occurs more frequently:

Case 1: If a classA has retained the delegate object of classB and classB has retained an instance of classA , then the problem here is that neither classA nor classB will ever be released from memory. Because classB's reference count cannot become zero unless classA is released and the classA object won't be released unless classB is deallocated.

Case 2: If "some class" has retained classA. and ClassB (which is the child of class A) has retained classA via a delegate. In this case, class A will not get released even if "some class" releases it because its retain count will not become zero. Since the parent's retain count has not reached zero, its dealloc is never called so it cannot send a release message to the child.

So if we assign the delegate instead of retaining it, there will be no problem of retain cycles.

Points to Note:
1. In this case, if the delegate is called after the object is already deallocated, your application will crash. So if your class is a delegate of some object which is going to be released, make sure you set that delegate to nil before releasing it.

2. Always call respondsToSelector on a delegate method to ensure it has been implemented before you call it.

Friday, March 25, 2011

Demystifying the viewDidLoad and viewDidUnload methods

The viewDidLoad is called after the controller's view is loaded into memory. The viewDidUnload is called when the controller's view is released from memory. Most of us think of these methods as just two calls that are invoked when the view controller is loaded and unloaded. However the important part of their definition is  the "loaded into memory" and "released from memory" parts. This means that these two functions play a vital role with regards to memory.Usually the viewDidLoad and viewDidUnload functions are called just once and that is the normal  behavior we are all familiar with. However, what we are going to discuss here is about the viewDidLoad and viewDidUnload being called the next time.

The viewDidUnload function will be called in low-memory situations. It is here that the view controller needs to release its views and subviews in order to decrease the memory usage of the application. Then later on the viewDidLoad will be called to recreate the views. So we need to write code in viewDidLoad which are necessary to reinitialize our UI.

What do we need to do?
1. Always make sure you set your outlets to nil in the viewDidUnload method.
eg:- self.myOutlet = nil;
In low-memory situations the xib will be released from memory but doing that alone won't reduce the memory consumption if all the outlets are still retained  in our code. So when we set the outlet to nil, we are invoking the setter function of the outlet which automatically releases it before setting nil to it.

2. Always try to write only UI initialisation code in viewDidLoad. If you write code to alloc/init a variable in your viewDidLoad, then when the method is invoked a second time the variable will be alloc/init'd again causing a memory leak. If you really do need to alloc/init your member variable here, do it only if it is not already allocated.

Note 1: Always set your outlets to nil using the 'self.' prefix otherwise you will only be setting nil to the variable thus causing a memory leak.

Note 2: This is applicable only for those outlets which have been retained using the @property(retain) call which is of course the standard method to handle your outlets.

Thursday, March 24, 2011

How to test your print functionality without a printer

AirPrint is the technology that allows you to wirelessly print your photos and documents directly from your iPad to any AirPrint enabled printer.

Sometime back we  were unable to test the print functionality of one of our apps because we didn't have a WiFi printer. We debated quite a bit about buying a WiFi printer. What held us back? Do we really need to invest in a WiFi printer which could be expensive and the purpose of which is just to test a print functionality?

Fortunately, apple has solved this problem for us in a very cost-effective manner.
If you install the iOS 4.2 SDK, you will have access to the print simulator tool. You can use this to easily test your printing functionality.

You will find the Printer Simulator software at Macintosh HD\Developer\Platforms\
iPhoneOS.platform\Developer\Applications. Just copy it to your applications folder and run it. Now all you have to do is open up your app and click the print option. Once the printing is complete, the printout automatically pops up on your screen.

Does it get any easier than that?

Are your Nib objects leaking memory?

Well, here are the 5 steps to correctly manage memory of nib objects.

Step 1: Declare outlets using the declared properties feature in the header file.

@property (nonatomic, retain) IBOutlet UILabel *nameLabel;

Step 2: Set your outlets to nil in viewDidUnload

- (void)viewDidUnload {
     self.nameLabel = nil;
     [super viewDidUnload];
}

Step 3: Release your outlet in the dealloc method and set it to nil

- (void)dealloc {
      // Release outlets and set outlet variables to nil.
     [nameLabel release];
     nameLabel = nil;
     [super dealloc];
}

What you should remember
1. The call to [super dealloc] should be the last line in your dealloc function and the call to [super viewDidUnload] should be the last line in your viewDidUnload function

Wednesday, March 23, 2011

Writing conditional code for different iOS versions

If you are going to use a function that is available only in later iOS versions, you will need to include conditionals. There are two types of conditionals - compile time and runtime conditionals. The purpose of conditionals is two-fold:

1. You need to use compile-time conditionals to prevent compile problems when running in older versions of the simulator.
2. You need the runtime check if you are going to build on a later version of simulator but are going to install on a device with an older version of iOS.

Before I dive into the details, let me give you an instance where this came in handy for me.

I needed to do an animation for resizing a view. I considered using the [UIView beginAnimation] and [UIView commitAnimation] functions. But when I consulted the docs, it said that the use of these functions are not recommended in iOS versions 4.0 and above.

Instead, they recommended the use of block animation functions (eg:- [UIView animateWithDuration]). So I wrote code for animating using the animateWithDuration function. I was able to successfully compile the program because I am using the 4.3 version simulator. The problem occurred when I tried to run it on an iOS 3.2 iPad. The app crashed when the animation call fired. So then I tried using a compilation conditional for the same as follows:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
      // code with animateWithDuration
#else
     // code with beginAnimation
#endif

I still got the crash because, since I am building on iOS v4.3, the animateWithDuration function still got compiled into the code. So that's when I finally used the run time check macros.

Here are the steps:

Step 1: Define a macro

#define IF_IOS4_OR_GREATER(...) \
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
{ \
__VA_ARGS__ \
}

I called my macro IF_IOS4_OR_GREATER because it checks if the runtime version is greater than 4.0 but you can call it anything you like.

Step 2: Call the macro where the runtime check is required. Instead of calling the macro directly in the source code, I decided to confine it to a specific function. I decided to call it isCompatibleOSVersion.

+ (BOOL)isCompatibleOSVersion{
    IF_IOS4_OR_GREATER
    (
     return YES;
     )
   
    return NO;
}

Step 3: Next all that is remaining is to do your processing based on this function call.

if ([self isCompatibleOSVersion]){
        // code with animateWithDuration 
}
else{
    // code with beginAnimation
}

Point to note:
If we want to include something only in OS versions prior to a a specific version, then we don't need the conditional compilation (since we still want the code to appear when compiled in a later version). In this case, only a runtime check is required.

Moral of the story:
Use conditionals wisely.