Objective C Blocks Sample Code
Written By: Debasis Das (19-Apr-2015)
In this post we will create sample codes for using blocks in an Cocoa Objective C Application
Block with 1 input Parameter and 1 return Type
int (^testBlock)(int) = ^int (int num)
{
int multiplier = 7;
return num * multiplier;
};
// NSLog(@"number %d",testBlock(10)); //This will print 70
Block with 2 input parameters and 1 return type as int
int (^addBlock)(int, int) = ^int(int va1, int val2)
{
return va1 + val2;
};
// NSLog(@"addBlock %d",addBlock(10,12)); //Prints 22
Two input parameters and block definition
-(void)blocksSampleThree{ float (^areaBlock)(float length, float breadth); //Declare the block variable //Create and assign the block areaBlock = ^float(float len, float brd) //Defines the return type to be float //areaBlock = ^(float len, float brd) //Does not define the return type. Defining the return type is not mandatory { return len * brd; }; float newArea = areaBlock(10.87,87.65); NSLog(@"newArea is %2.2f",newArea); //Prints 952.76 }
Block with No input Parameters
u_int32_t (^randomNumber)(void) = ^{
return arc4random();
};
// NSLog(@"random number is %d",(int)randomNumber);
Closures
-(void)blocksSampleFour{ //Blocks can access to non local variables defined in the blocks enclosing lexical scope but outside the block itself NSString *firstName = @"John"; NSString * (^getFullName)(NSString *lastName) = ^(NSString *lName){ // firstName = @"Jane"; //Variable cannot be assigned here untill and unless the variable is declared as a block type return [firstName stringByAppendingFormat:@" %@",lName]; }; NSLog(@"Full Name is %@",getFullName(@"Doe")); //Full Name is John Doe //Non Local Variables are copied and stored with the blocks are constants (read only) //The non local variable cannot be modified firstName = @"Jane"; NSLog(@"Full Name after first name is changed is still %@",getFullName(@"Doe")); //Full Name after first name is changed is still John Doe }
Changing a non local variable within a block
-(void) blocksSampleFive{ __block NSString *title = @"Mr"; //The block modifier serves as a memory between multiple calls to a block NSString *(^getCompleteName)(NSString *name) = ^(NSString *name){ title = @"Ms"; //If we did not use the block modifier this will continue to hold Mr return [title stringByAppendingFormat:@" %@",name]; }; NSLog(@"Complete Name is %@",getCompleteName(@"Jane Doe")); //Complete Name is Ms Jane Doe }
Blocks as Method Parameters
-(void)blocksSampleSix{ NSArray *stringsArray = @[ @"string 1", @"String 21", @"string 12", @"String 11", @"String 02" ]; static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch; NSLocale *currentLocale = [NSLocale currentLocale]; NSComparator finderSortBlock = ^(id string1, id string2) { NSRange string1Range = NSMakeRange(0, [string1 length]); return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale]; }; //In the below line of code the finderSortBlock is sent as a method parameter NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock]; NSLog(@"finderSortArray: %@", finderSortArray); /* Output: finderSortArray: ( "string 1", "String 02", "String 11", "string 12", "String 21" ) */ }
Enumerate Objects Using Block
-(void)blocksSampleSeven{
NSArray *numberArray = @[@12,@13,@10,@9,@100,@109,@432];
[numberArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"index = %d value = %d %@",idx,[obj intValue],stop?@"Yes":@"No");
}];
//index = 0 value = 12 Yes
//index = 1 value = 13 Yes
//index = 2 value = 10 Yes
//index = 3 value = 9 Yes
//index = 4 value = 100 Yes
//index = 5 value = 109 Yes
//index = 6 value = 432 Yes
}
A simple block that simply executes without taking any input parameter or returning any value
-(void)blocksSampleEight { void (^callSimpleBlock)(void) = ^{ NSLog(@"This is a block, Takes nothing, gives nothing"); }; callSimpleBlock(); }
Test Block to find the index of objects in an array present in a NSSet
-(void)testSetAndArrays{
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
test = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) {
if ([filterSet containsObject: obj]) {
return YES;
}
}
return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
NSLog(@"indexes: %@", indexes);
//indexes: [number of indexes: 2 (in 2 ranges), indexes: (0 3)]
}
Block Completion- Sample 1
-(void)blocksSampleNine{ [self doSomething:^{ NSLog(@"This is the completion block. This will get called after the job at doSomething is completed"); }]; } typedef void (^EndBlock)(); // A method that returns a completion block -(void)doSomething:(EndBlock)completionBlock { NSLog(@"Do Something first"); //This can be a lengthy calculation here. //Once this is done then the completion block is called completionBlock(); NSLog(@"Then may be something else"); }
Block Completion- Sample 2
-(void)blocksSampleTen{
[self countToHundredAndReturnCompletionBLock:^(BOOL completed){
if(completed)
{
NSLog(@"Hundred Counts Finished");
}
else
{
NSLog(@"Actually Completed but showing not completed. Returning NO here");
}
}];
}
// A method that returns a completion block
-(void)countToHundredAndReturnCompletionBLock:(void (^)(BOOL))completed
{
int x = 1;
while (x < 100) {
x++;
}
completed(YES);
}
Display Progress Indicator and Hide Progress Indicator in Completion Block
/* Using Blocks to Show/hide progress indicator while a task is being executed A simple block implementation to show a progress indicator while a task is in progress. Once the task is completed the progress indicator needs to hide. */ -(void)blocksSampleEleven{ [self getDataFromDatabase:self]; } /* The order of execution is as follows -[AppDelegate getDataFromDatabase:] -[AppDelegate showProgressIndicator] -[AppDelegate doActualDatabringingActivity:] -[AppDelegate hideProgressIndicator] */ -(IBAction)getDataFromDatabase:(id)sender { NSLog(@"%s",__func__); [self showProgressIndicator]; [self doActualDatabringingActivity:^{ [self hideProgressIndicator]; /* Avoid Strong reference cycles when capturing self inside blocks Capturing a strong reference to self creates a strong reference cycle. //To avoid this, its better to capture a wek reference to self InterfaceName *__weak weakSelf = self; [weakSelf doSomething]; */ }]; } -(void)doActualDatabringingActivity:(void (^) (void))callBackBlock { NSLog(@"%s",__func__); callBackBlock(); } -(void)showProgressIndicator { NSLog(@"%s",__func__); } -(void)hideProgressIndicator { NSLog(@"%s",__func__); }
Get Data from WebService / Database using Blocks
-(void)findDataFromWebService{ [[SampleService sharedInstance] fetchCompanyDataWithCompletionBlock:^(NSDictionary *companyRecord, NSError *error) { if (!error) { NSLog(@"companyRecord %@",companyRecord); } else { [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [NSApp presentError:error]; }]; } }]; } @end
#import <Foundation/Foundation.h> @interface SampleService : NSObject +(SampleService *)sharedInstance; -(void) fetchCompanyDataWithCompletionBlock:(void(^)(NSDictionary *companyRecord, NSError *error)) completionBlock; @end
// SampleService.m // ObjcBlocks // Created by Debasis Das on 4/19/15. #import "SampleService.h" @implementation SampleService +(SampleService *) sharedInstance { static SampleService *_sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [[SampleService alloc] init]; }); return _sharedInstance; } -(void) fetchCompanyDataWithCompletionBlock:(void(^)(NSDictionary *companyRecord, NSError *error)) completionBlock { //Create a service request //Post the service request //Actual Data receiveing goes here NSDictionary *companyRecord = [NSDictionary dictionaryWithObjectsAndKeys:@"1000",@"companyId", nil]; // NSError *error = nil; NSError *error = [NSError errorWithDomain:@"Database Connection probably failed" code:7654310 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Some Error Desc",@"errorDesc", nil]]; completionBlock (companyRecord, error); }
Hi … Great Tutorial for beginners..
Hi If u could elaborate why we are going to use shared instance. how it is useful. That would be good to understand end to end. Thanks
Hi Imran,
The sample service class is a singleton and thus the sharedInstance.
The only objective of the SampleService class is to get data from the db through a webservice. We can create a SampleService as subclass of NSObject and do not make it a singleton and still things will work, however everytime we need to call the fetchCompanyDataWithCompletionBlock we have to create an object of SampleService.
I am writing another article to fetch data from webservice using two different approaches. (Call Back vs NSURLSession Delegate). Once you go through this new article, I think things will be much more clearer.
Thanks
Debasis
Hi Imran,
Here is the sample code that explains the concept using an end to end use case of NSURLSession
NSURLSession Sample Code
Thanks
Debasis
Hi Deb, Thanks for sharing it. Really well explained tutorial.
Hi ,
What if “companyRecord” itself contained data and was passed as argument . So how do i use itinside the block in this case?