Sunday, July 21, 2013

Objective-C Categories

Categories allow you to add methods to existing classes. The category allows new methods to be added to an existing class.
  • Categories let you add application or domain specific methods to existing classes. It can be quite powerful and convenient.
  • The clue is in the restriction that categories only add methods, you can't add variables to a class using categories. If the class needs more properties, then it has to be subclassed.(edit: you can use associative storage, I believe).
  • Categories are a nice way to add functionality while at the same time conforming to an object oriented principle to prefer composition over inheritance.

                       One of favorite illustrations of Objective-c categories in action is NSString. NSString is defined in the Foundation framework, which has no notion of views or windows. However, if you use an NSString in a Cocoa application you'll notice it responds to messages like – drawInRect:withAttributes:.


                                      Steps to create Categories


Step 1: Create the Category

Now that your project is set up, let's create a category that adds additional functionality to the NSStringclass. Click File > New > File and choose a Cocoa Touch Objective-C category from the window. Click "Next." Name your category "RemoveNums" and select NSString from the "Category on" drop down menu (you may need to type this in manually). Click "Next" followed by "Create."



 Declare Category:









Step 2:Declare the Category Method:

Back in your Xcode project, click "NSString+RemoveNums.h" to view the new category's header file. Add the following code to the interface to declare the method.


#import

@interface NSString (removeNumbers)
{
    
}

- (NSString *)removeNumbersFromString:(NSString *)string;

@end

Implement the Category Method:


#import "NSString+removeNumbers.h"

@implementation NSString (removeNumbers)

- (NSString *)removeNumbersFromString:(NSString *)string
{
    /*--
     - Implemetation logic for new category method
     * This category method creates a NSCharacterSet of 0-9
     * It trims any occurences of number characters from the input string
     * It returns the new string value to the receiver
     --*/
    
    NSString *trimmedString = nil;
    
    NSCharacterSet *numbersSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
    
    trimmedString = [string stringByTrimmingCharactersInSet:numbersSet];
    
    return trimmedString;
}

@end

 First we define an NSCharacterSetof the numbers zero through nine which we'll use as a reference to compare against the original input string. In this case, the original string "Tekglintsoftsol iPhone Training 12345" will have the numbers "12345" removed from the string because the category method uses the NSString method stringByTrimmingCharactersInSet:.

my NSString+removeNumbers.h category .h and .m is over now i have created one class i.e 


MyClass 

After declaring the class i have imported Category into Myclass. the declaration is look like this 

#import "NSString+removeNumbers.h"

#import

@interface MyClass : NSObject
{
    
}

@end


After importing the Category i have created method declaration in Myclass interface .h

#import "NSString+removeNumbers.h"

#import

@interface MyClass : NSObject
{
    
}

-(NSString *)removeNumbersinString:(NSString *) string;

@end


MyClass Implementation .m 

#import "MyClass.h"

@implementation MyClass

-(NSString *)removeNumbersinString:(NSString *)string
{
    //Create the string
    
    NSString *stringWithNums =string;
    
    NSLog(@"stringWithNums         --> %@",stringWithNums);
    
    //Run string through category method
    
    stringWithNums = [stringWithNums removeNumbersFromString:stringWithNums];
    
    //Output trimmed string to the console
    
    NSLog(@"trimmed stringWithNums --> %@",stringWithNums);

    
    return stringWithNums;
}
@end

The above removeNumbersFromString is Category method whenever we use this method the original string "Tekglintsoftsol iPhone Training 12345" will have the numbers "12345" removed from the string because the category method uses the NSString method stringByTrimmingCharactersInSet:.

          After MyClass implementation is over now i am importing MyClass into main method and creating the MyClass object my code is shown below.

#import

#import "MyClass.h"


int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        MyClass *myClassObject=[[MyClass alloc]init];
        
        
    }
    return 0;
}


Step 3:Test the Category 



#import

#import "MyClass.h"


int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        MyClass *myClassObject=[[MyClass alloc]init];
        
        [myClassObject removeNumbersinString:@"Tekglint iPhone Training 12345"];
        
        
    }
    return 0;
}

 The local variablestringWithNums contains a combination of letters and numbers. The next line takes the string variable and runs it through the category method removeNumbersFromString. Finally, NSLog outputs the returned value of the newly trimmed string without any numbers.

Step 4:Run The Application

Click Product > Run, or click the "Run" arrow in the top left corner, to test the code. Notice the console shows the original input string, "Tekglint iPhone Training 12345" as well as the string after it has been altered by the category method and the numbers have been removed.

2013-07-22 12:37:33.118 RemoveNumbers[1062:303] stringWithNums         --> Tekglint iPhone

Training 12345

2013-07-22 12:37:33.128 RemoveNumbers[1062:303] trimmed stringWithNums --> Tekglint iPhone Training 


Step 5: Conclusion 

Subclassing is one way to add functionality to an object, but avoiding unnecessary subclassing by using a category will help reduce the amount of code and keep your projects more organized. There are a number of scenarios where using a category is beneficial.

Friday, July 19, 2013

Objective-C Exception Handling and Errors

Two distinct types of problems can arise while an iOS or OS X application is running. Exceptions represent programmer-level bugs like trying to access an array element that doesn’t exist. They are designed to inform the developer that an unexpected condition occurred. Since they usually result in the program crashing, exceptions should rarely occur in your production code.

On the other hand, errors are user-level issues like trying load a file that doesn’t exist. Because errors are expected during the normal execution of a program, you should manually check for these kinds of conditions and inform the user when they occur. Most of the time, they should not cause your application to crash.

Exceptions:
Exceptions are represented by the NSException class. It’s designed to be a universal way to encapsulate exception data, so you should rarely need to subclass it or otherwise define a custom exception object. NSException’s three main properties are listed below.
PropertyDescription
nameAn NSString that uniquely identifies the exception.
reasonAn NSString that contains a human-readable description of the exception.
userInfoAn NSDictionary whose key-value pairs contain extra information about the exception. This varies based on the type of exception.
It’s important to understand that exceptions are only used for serious programming errors. The idea is to let you know that something has gone wrong early in the development cycle, after which you’re expected to fix it so it never occurs again. If you’re trying to handle a problem that’s supposed to occur, you should be using an error object, not an exception.
Exceptions can be handled using the standard try-catch-finally pattern found in most other high-level programming languages. First, you need to place any code that might result in an exception in an@try block. Then, if an exception is thrown, the corresponding @catch()block is executed to handle the problem. The @finally block is called afterwards, regardless of whether or not an exception occurred.
The following main.m file raises an exception by trying to access an array element that doesn’t exist. In the @catch() block, we simply display the exception details. The NSException *theException in the parentheses defines the name of the variable containing the exception object.

#import

int main(int argc, const char * argv[])
{    @autoreleasepool    
{

 //Exception implementation      
  NSArray *inventory =[NSArray arrayWithObjects:@"one",@"two",@"three",nil];   
           
  int selectedIndex = 3;              
  @try        
{            
   NSString *number =[inventory objectAtIndex:selectedIndex];     

   NSLog(@"The selected object is: %@", number);                   
 }        
@catch(NSException *theException) //throws an exception     
{    
  NSLog(@"An exception occurred: %@", theException.name);    

  NSLog(@"Here are some details: %@", theException.reason); 
     
  }      
  @finally      
  {          
  NSLog(@"Executing finally block");        
  }                     

}    
return 0;

}

You can observe the above program i am writing the code in try() block after executing my program i am getting the out put as shown below.

2013-07-19 13:51:45.186 Exception Handling[1313:303] An exception occurred: NSRangeException

2013-07-19 13:51:45.188 Exception Handling[1313:303] Here are some details: *** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]

2013-07-19 13:51:45.188 Exception Handling[1313:303] Executing finally block

we get the exception name and reason using NSException class properties.

In the real world, you’ll want your @catch() block to actually handle the exception by logging the problem, correcting it, or possibly even promoting the exception to an error object so it can be displayed to the user. The default behavior for uncaught exceptions is to output a message to the console and exit the program.
Objective-C’s exception-handling capabilities are not the most efficient, so you should only use @try/@catch() blocks to test for truly exceptional circumstances. Do not use it in place of ordinary control flow tools. Instead, check for predictable conditions using standard if statements.

Built-In Exceptions

The standard iOS and OS X frameworks define several built-in exceptions. The complete list can be found here, but the most common ones are described below.
Exception NameDescription
NSRangeExceptionOccurs when you try to access an element that’s outside the bounds of a collection.
NSInvalidArgumentExceptionOccurs when you pass an invalid argument to a method.
NSInternalInconsistencyExceptionOccurs when an unexpected condition arises internally.
NSGenericExceptionOccurs when you don’t know what else to call the exception.

Errors :

Since errors represent expected problems, and there are several types of operations that can fail without causing the program to crash, they are much more common than exceptions. Unlike exceptions, this kind of error checking is a normal aspect of production-quality code.
The NSError class encapsulates the details surrounding a failed operation. Its main properties are similar to NSException.
PropertyDescription
domainAn NSString containing the error’s domain. This is used to organize errors into a hierarchy and ensure that error codes don’t conflict.
codeAn NSInteger representing the ID of the error. Each error in the same domain must have a unique value.
userInfoAn NSDictionary whose key-value pairs contain extra information about the error. This varies based on the type of error.
The userInfo dictionary for NSError objects typically contains more information than NSException’s version. Some of the pre-defined keys, which are defined as named constants, are listed below.
KeyValue
NSLocalizedDescriptionKeyAn NSStringrepresenting the full description of the error. This usually includes the failure reason, too.
NSLocalizedFailureReasonErrorKeyA brief NSStringisolating the reason for the failure.
NSUnderlyingErrorKeyA reference to anotherNSError object that represents the error in the next-highest domain.
Depending on the error, this dictionary will also contain other domain-specific information. For example, file-loading errors will have a key called NSFilePathErrorKey that contains the path to the requested file.
Note that the localizedDescription and localizedFailureReasonmethods are an alternative way to access the first two keys, respectively. These are used in the next section’s example.

Handling Errors :

Errors don’t require any dedicated language constructs like @try and@catch(). Instead, functions or methods that may fail accept an additional argument (typically called error) that is a reference to anNSError object. If the operation fails, it returns NO or nil to indicate failure and populates this argument with the error details. If it succeeds, it simply returns the requested value as normal.
Many methods are configured to accept an indirect reference to anNSError object. An indirect reference is a pointer to a pointer, and it allows the method to point the argument to a brand new NSErrorinstance. You can determine if a method’s error argument accepts a indirect reference by its double-pointer notation: (NSError **)error.
The following snippet demonstrates this error-handling pattern by trying to load a file that doesn’t exist via NSString’sstringWithContentsOfFile:encoding:error: method. When the file loads successfully, the method returns the contents of the file as anNSString, but when it fails, it directly returns nil and indirectly returns the error by populating the error argument with a new NSError object
#import

int main(int argc, const char * argv[])
{

    @autoreleasepool
    {
         
        //Error implementation
        
        NSString *fileToLoad = @"/Users/tekglintsoftsol/Desktop/mynote.rtf";
        
        
        NSError *error;
        
        NSString *content = [NSString stringWithContentsOfFile:fileToLoad
                                                      encoding:NSUTF8StringEncoding
                                                       error:&error];
        
        if (content == nil)
        {
            // Method failed
           NSLog(@"Error loading file %@!", fileToLoad);
            NSLog(@"Domain: %@", error.domain);
            NSLog(@"Error Code: %ld", error.code);
            NSLog(@"Description: %@", [error localizedDescription]);
            NSLog(@"Reason: %@", [error localizedFailureReason]);
            
        }
        else
        {
            // Method succeeded
            NSLog(@"Content loaded!");
            NSLog(@"%@", content);
        }
        
    }
    return 0;
}

Notice how the we had to pass the error variable to the method using the reference operator. This is because the error argument accepts a double-pointer. Also notice how we checked the return value of the method for success with an ordinary if statement. You should only try to access the NSError reference if the method directly returns nil, and you should never use the presence of an NSError object to indicate success or failure. 

The above i am trying to access the file i.e mynotes.rtf but i am giving the file name as  mynote.rtf we get the out put as like below.

2013-07-19 14:05:48.245 Exception Handling[1333:303] Error loading file /Users/tekglintsoftsol/Desktop/mynote.rtf!

2013-07-19 14:05:48.247 Exception Handling[1333:303] Domain: NSCocoaErrorDomain

2013-07-19 14:05:48.247 Exception Handling[1333:303] Error Code: 260

2013-07-19 14:05:48.260 Exception Handling[1333:303] Description: The file “mynote.rtf” couldn’t be opened because there is no such file.

2013-07-19 14:05:48.260 Exception Handling[1333:303] Reason: The file doesn’t exist.

In this case NSError will return nil or NO value because it is an error for loading file path. now i am changing the path as @"/Users/tekglintsoftsol/Desktop/mynotes.rtf" we get the out put as shown like below.

2013-07-19 14:09:17.591 Exception Handling[1349:303] Content loaded!

2013-07-19 14:09:17.593 Exception Handling[1349:303] 

{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural

\f0\fs24 \cf0 hai welcome to tekglint }

Errors represent a failed operation in an iOS or OS X application. It’s a standardized way to record the relevant information at the point of detection and pass it off to the handling code. Exceptions are similar, but are designed as more of a development aid. They generally should not be used in your production-ready programs.

Objective-C Protocols

A protocol is a group of related properties and methods that can be implemented by any class. objective-c not supported multiple inheritance directly by achieving multiple inheritance we are using Protocols.


Like class interfaces, protocols typically reside in a .h file. To add a protocol to your Xcode project, navigate to File > New> File… or use the Cmd+N shortcut. Select Objective-C protocol under theiOS > Cocoa Touch category.





Creating a protocol in Xcode


In this module, we’ll be working with a protocol called StreetLegal. Enter this in the next window, and save it in the project root.





Example program using protocol:  my protocol name is Printing lets see below how to declare a protocol and methods.



#import


@protocol Printing <NSObject>

-(void) print;

@end

the above protocol declaration is over and i have declare one method -

if the protocol method is @Required  ex:

@Required
-(void)display

whenever you're using this protocol in child classes u must be implementing .m in child class.
suppose if declare a method using @Optional Ex:

@Optional
-(void)displayAll

this method is our optional to implementing in child class , below i show one example

#import 

@protocol Printing <NSObject>

@required

-(void) print;

@optional

-(void)displayAll;

@end


now i am taking two classes 1.Fraction and 2.Complex using this protocol how to access the protocol methods in classes and how to declare a protocol in classes below we can see those implementations.

Fraction class .h

#import "Printing.h"    //protocol imported to our fraction class

#import

@interface Fraction : NSObject<Printing>
{
   
}

@property (nonatomic) int numerator;

@property (nonatomic) int denominator;

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;

@end

Fraction class .m

#import "Fraction.h"

@implementation Fraction

@synthesize numerator;

@synthesize denominator;

-(Fraction *)initWithNumerator:(int)n denominator:(int)d
{
  
   self = [super init];
   
   if ( self ) {
       self.numerator = n;
       self.denominator = d;
   }
  
   return self;
}

-(void) print
{
   NSLog(@ "%i/%i   ", self.numerator, self.denominator );
}


@end


you can observe above we are using print method (printing protocol) and we have print the numerator and denominator using print protocol method

2. Complex class .h

#import "Printing.h"

#import 

@interface Complex : NSObject <Printing>
{
   
}

@property (nonatomic) double real;
@property (nonatomic) double imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i;

@end

Complex class .m
#import "Complex.h"

@implementation Complex

@synthesize real;
@synthesize imaginary;

-(Complex*) initWithReal: (double) r andImaginary: (double) i
{
   self = [super init];
   
   if ( self ) {
       self.real = r;
       self.imaginary = i;
   }
   
   return self;
}

-(void) print
{
   NSLog(@"%f + %fi", self.real, self.imaginary );
}

@end

we are finishing the two classes .h and .m using protocol now we are importing these two classes into main method.

#import 



#import "Fraction.h"
#import "Complex.h"

int main(int argc, const char * argv[])
{

   @autoreleasepool {
       
       // insert code here...
       
       Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
       Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
       
       id <Printing> printable;
       
       // print it
       printable = frac;
       
       NSLog(@"The fraction is: " );
       
        [frac print];
       
        NSLog(@"\n");
       
       // print complex
       
       printable = comp;
       
       NSLog(@"The complex number is: " );
       
       [printable print];
       
        NSLog(@"\n");
       
   }
   return 0;
}

After importing the classes creating the objects for two classes and you can observe the above initWith is a class constructor type. we are assigning the vales

Numerator: 3 denominator: 10
Real: 5 andImaginary: 15

using class objects call the instance method i.e print (printing protocol method)

and we get the output as shown below.

2013-07-19 13:07:11.606 Protocols[1149:303] The fraction is:
2013-07-19 13:07:11.608 Protocols[1149:303] 3/10   
2013-07-19 13:07:11.613 Protocols[1149:303]
2013-07-19 13:07:11.614 Protocols[1149:303] The complex number is:
2013-07-19 13:07:11.614 Protocols[1149:303] 5.000000 + 15.000000i
2013-07-19 13:07:11.615 Protocols[1149:303]

this is the way of using protocols in our program, we can create multiple protocols and we can access multiple protocols in the class.

Protocols In The Real World

A more realistic use case can be seen in your everyday iOS application development. The entry point into any iOS app is an “application delegate” object that handles the major events in a program’s life cycle. Instead of forcing the delegate to inherit from any particular superclass, the UIKit Framework just makes you adopt a protocol:
@interface YourAppDelegate : UIResponder
As long as it responds to the methods defined byUIApplicationDelegate, you can use any object as your application delegate. Implementing the delegate design pattern through protocols instead of subclassing gives developers much more leeway when it comes to organizing their applications.