Wednesday, May 1, 2013

A neat little trick to check for memory leaks

There are several tools and methods to check for memory leaks in your iOS apps. This one is something I came across on the 200Monkeys site and loved for its simplicity. If you are using ARC, you will realize it is harder to track or fix memory leaks. This may not be useful all of the time but may come in handy in particular scenarios.

Suppose your app allocates multiple instances of a class and you trusted ARC to handle the deallocations of these objects when they are no longer needed. But after that, if you find that there are memory leaks in your app, the first thing you might want to check is if all those instances have been deallocated correctly. Just put the following code in the class whose instances you want to track:




Now just look for the 'Total freed' logs and you can figure out if all your instances have been freed.

Wednesday, April 24, 2013

Why is my view disappearing in landscape orientation when using Autolayout?

Have you come across this particular scenario where you enable Autolayout, drag a view onto the parent view, change the orientation of the parent view to landscape and the view just disappears? If you rotate it back to portrait, the view comes back. I used to find this really confusing. There is a very logical explanation as to why this happens.

Here is my parent view with the view I dragged on to it.


Simple enough! You probably already know that the Autolayout feature uses constraints to figure out where a particular object is located within a view. So here is what the constraints look like for my view.

The parent view has a size of 320 x 460 in portrait mode which changes to 480 x 300 in landscape mode. So the reason for the disappearing view is quite simple.

When we have constraints for all sides of the view, it means that the child view will be resized automatically to meet those constraints regardless of what orientation it is in. The width of the child view is the width of the parent view minus the sum of the horizontal space constraints. Similarly, the height of the child view is the height of the parent view minus the sum of the vertical space constraints. In the portrait mode of this particular example, the child view has a width of 320 - (85 + 75) = 160, and a height of 460 - (95 + 205) = 160. And to verify that, all you have to do is select the child view and open up the size inspector and sure enough there it is.

Now let's see what happens when the view is rotated to landscape. As per our previous calculations the width of the rotated view should be 480 - (85 + 75) = 320 and the height of the new view will be 300 - (95 + 205) = 0. That's why you don't see the view in landscape, its height is zero. If you select the view in the document outline, you can see that the view has no height.

This problem occurs because we have 4 constraints describing how far away the child view should be from the 4 sides of the parent view and since those constraints are not flexible, the child view has to be resized to meet those constraints. If you want to see your child view in the same size even in the landscape mode, here is what you do.

1. Change the orientation to portrait and you can see your view.
2. Fix the width and height of your view by selecting the child view and selecting the menu option Editor->Pin->Width and Editor->Pin->Height.
3. Now that the height and width of your child view is fixed, remove the constraints for the bottom and right of the child view.

This is what the list of constraints looks like now:

Now rotate the view to landscape and voila your view is still visible.

Thursday, April 18, 2013

How to track allocations and deallocations when using ARC

If you are new to ARC(Automatic Reference counting), you might feel that you have lost control of the allocations and deallocations of objects. Well, the truth is, that's why ARC was invented. So you don't need to track the allocations and deallocations. But sometimes for debugging purposes it might help to know when a particular object is being allocated and deallocated. A very simple method for this is to add the following lines of code to the class whose object you want to track. The example below is for a view controller class



You will observe that as you run your app, the init and dealloc log messages will be displayed in the xcode console as and when your object gets allocated and deallocated.

If you happened to get some error messages saying that an initWithCoder function could not be found, then just override the init function instead of initWithCoder. Classes derived from NSObject would not have an initWithCoder function unless it implements the NSCoding protocol.

You shouldn't have to track allocations and deallocations regularly if you are using ARC because ARC does a pretty good job of handling it. But in the rare occasion when you feel something is not going as planned, it doesn't hurt to double check.

Did you find this helpful? Do you have a special way of tracking memory? Do let me know.

Why don't I see the autosizing feature in the new xcode 4.2?

In the new XCode 4.2, when you start a new project with the default settings, you may not find the autosizing feature. This is what the size inspector will probably look like:


Nothing to worry. There is a very easy way to display it. Select your storyboard file and then on the file inspector. You will notice that the 'Use Autolayout' option is selected. Well, that is exactly why you don't see the autosizing view. Just uncheck the 'Use Autolayout' option. Now if you check the size inspector, voila, there it is.


Hope this helps.

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!