⬆️ ⬇️

About blocks and their use in Objective-C part 2

Topic Continuation - On blocks and their use in Objective-C part 1 .



Many of those who first encounter blocks (or closures) ask the question “why? If it is possible without them. ” Yes you can. But the use of blocks has quite a few advantages, and the first of them is a significant saving on the amount of code, and consequently on writing time and support. I will continue to talk examples.



Content:



1. Work with containers on the example of NSArray.

2. Guards on the example of UITableView.

3. Using blocks instead of classes using the example of scheduled operations.

4. Blocks instead of delegates in UIAlertView.

5. UIView animation, sequence of animations.

6. Asynchronous operations and management. Rewrite the example with animations.



1. Work with containers on the example of NSArray.


The most frequently cited example of using blocks is to work with containers. This topic will not be an exception, we will look at solutions of some standard tasks using blocks.

')

Task1


Write a function that creates an array of numbers from an array of strings, each element of which is a long corresponding line of the incoming array.



Solution1:

NSArray* stringsLengths( NSArray* strings_ )

{

NSMutableArray* strings_lengths_ =

[ NSMutableArray arrayWithCapacity: [ strings_ count ] ];

for ( NSString* string_ in strings_ )

{

NSNumber* length_ = [ NSNumber numberWithUnsignedInt: [ string_ length ] ];

[ strings_lengths_ addObject: length_ ];

}

return [ NSArray arrayWithArray: strings_lengths_ ];

}




* This source code was highlighted with Source Code Highlighter .


Solution2 with blocks:

NSArray* stringsLengths( NSArray* strings_ )

{

return [ strings_ map: ^( id string_ )

{

return (id)[ NSNumber numberWithUnsignedInt: [ string_ length ] ];

} ];

}




* This source code was highlighted with Source Code Highlighter .


Task2


An array of structures with the following interface is specified:

@ interface Element : NSObject



@property ( nonatomic, retain, readonly ) NSArray* subElements;



@end




* This source code was highlighted with Source Code Highlighter .
You need to create a new array that contains all the elements of all subElements.



Solution1:

NSArray* allSubElements( NSArray* elements_ )

{

NSMutableArray* result_ = [ NSMutableArray array ];



for ( Element* element_ in elements_ )

{

NSArray* object_items_ = element_.subElements;

[ result_ addObjectsFromArray: object_items_ ];

};



return [ NSArray arrayWithArray: result_ ];

}




* This source code was highlighted with Source Code Highlighter .


Solution2 with blocks:

NSArray* allSubElements( NSArray* elements_ )

{

return [ elements_ flatten: ^( id element_ )

{

return [ element_ subElements ];

} ];

}




* This source code was highlighted with Source Code Highlighter .


A few more convenient extensions to the NSArray class can be found in the NSArray + BlocksAdditions.m file .



2. Guards on the example of UITableView.


If it is necessary to perform several content updates in a UITableView with animations (for example, add one element and delete another), then everything would look neat in the resulting animation, these actions must be placed within the call of two methods: beginUpdates and endUpdates. In this code, you can make several errors, for example:

[ self beginUpdates ];



[ self.tableView deleteRowsAtIndexPaths: delete_index_pathes_

withRowAnimation: UITableViewRowAnimationBottom ];



// condition_ == true, endUpdates

if ( condition_ )

return ;



[ self.tableView insertRowsAtIndexPaths: insert_index_pathes_

withRowAnimation: UITableViewRowAnimationTop ];



[ self endUpdates ];




* This source code was highlighted with Source Code Highlighter .


In such cases, as in the case of working with files or other resources that need to be released, so-called guards-guards come to the rescue, which are easily realized with the help of blocks. Add the UITableView class extension with the withinUpdates method:

@ interface UITableView (BlocksAdditions)



-( void )withinUpdates:( void (^)( void ) )block_;



@end



@implementation UITableView (BlocksAdditions)



-( void )withinUpdates:( void (^)( void ) )block_

{

[ self beginUpdates ];



@ try

{

block_();

}

@ finally

{

[ self endUpdates ];

}

}



@end




* This source code was highlighted with Source Code Highlighter .


fix the bug with animations:

[ self.tableView withinUpdates: ^( void )

{

[ self.tableView deleteRowsAtIndexPaths: delete_index_pathes_

withRowAnimation: UITableViewRowAnimationBottom ];



if ( condition_ )

return ;



[ self.tableView insertRowsAtIndexPaths: insert_index_pathes_

withRowAnimation: UITableViewRowAnimationTop ];

} ];




* This source code was highlighted with Source Code Highlighter .


3. Using blocks instead of classes using the example of scheduled operations.


The problem to be solved in this example is not directly related to the blocks, but it shows how well the problem with their use can be solved.



Those who worked closely with the methods: - [NSObject performSelector: withObject: afterDelay:] and + [NSTimer timerWithTimeInterval: target: selector: userInfo: repeats:] I think you noticed that the “retain” method will be called for “target” at the moment of making a pending call , and "release" if the planned actions will no longer be invoked. As practice shows, this behavior is not very convenient, since it often requires writing additional logic on the + [NSObject cancelPreviousPerformRequestsWithTarget:] and - [NSTimer invalidate] calls to cancel scheduled calls, and the subsequent possibility of releasing the target object.

Here we come to the idea that it would be more convenient to be able to create a deferred call that would not cause a “retain” and would cancel itself when the “target” was deleted from memory.

Our goal will be to write a method that works in the manner described above with this interface:

@ interface NSObject (Scheduler)



-( void )performSelector:( SEL )selector_

timeInterval:( NSTimeInterval )time_interval_

userInfo:( id )user_info_

repeats:( BOOL )repeats_;



@end



* This source code was highlighted with Source Code Highlighter .


First, let's implement the JFFScheduler class with this interface:

//

typedef void (^JFFCancelScheduledBlock) ( void );

//

typedef void (^JFFScheduledBlock) ( JFFCancelScheduledBlock cancel_ );



@ interface JFFScheduler : NSObject



// ""

+(id)scheduler;



// " "

+(id)sharedScheduler;



//

// -

-(JFFCancelScheduledBlock)addBlock:( JFFScheduledBlock )block_

duration:( NSTimeInterval )duration_;



// , dealloc JFFScheduler

-( void )cancelAllScheduledOperations;



@end





* This source code was highlighted with Source Code Highlighter .


Method implementation - (JFFCancelScheduledBlock) addBlock :( JFFScheduledBlock) block_ duration :( NSTimeInterval) duration_

-(JFFCancelScheduledBlock)addBlock:( JFFScheduledBlock )block_

duration:( NSTimeInterval )duration_

{

//

JFFSimpleBlockHolder* cancel_block_holder_ = [ JFFSimpleBlockHolder simpleBlockHolder ];



block_ = [ [ block_ copy ] autorelease ];

// block_

// performBlock

void (^schedule_block_) ( void ) = [ [ ^

{

block_( cancel_block_holder_.simpleBlock );

} copy ] autorelease ];



// "target"

__block NSTimer* timer_ = [ NSTimer scheduledTimerWithTimeInterval: duration_

target: schedule_block_

selector: @selector( performBlock )

userInfo: nil

repeats: YES ];



__block NSObject* cancel_ptr_ = nil;

__block JFFScheduler* scheduler_ = self;



//

cancel_block_holder_.simpleBlock = ^

{

if ( scheduler_ )

{

[ timer_ invalidate ];

//

[ scheduler_.cancelBlocks removeObject: cancel_ptr_ ];

scheduler_ = nil;

}

};



cancel_ptr_ = (id)cancel_block_holder_.simpleBlock;

// dealloc

[ self.cancelBlocks addObject: cancel_ptr_ ];



return cancel_block_holder_.simpleBlock;

}




* This source code was highlighted with Source Code Highlighter .


All JFFScheduler class implementation code .



Then everything would work, we need a few additional methods:

1. - [NSObject performBlock] - execute the block

@implementation NSObject (PerformBlock)



//

-( void )performBlock

{

void * self_ = self;

JFFSimpleBlock block_ = (JFFSimpleBlock)self_;

block_();

}



@end



//

[ ^ {

NSLog( @"test" );

} performBlock ];




* This source code was highlighted with Source Code Highlighter .


2. - [NSString numberOfCharacterFromString:] the number of occurrences of the character specified in the string. Example:

NSLog( @"num of \":\" - %d" , [ @":test:" numberOfCharacterFromString: @":" ] );



* This source code was highlighted with Source Code Highlighter .


types - num of ":" - 2



3. - [NSObject addOnDeallocBlock:] - add a block that should be executed when the owner is removed from memory (in the dealloc method). Example:

NSObject* object_ = [ [ NSObject alloc ] init ];

[ object_ addOnDeallocBlock: ^

{

NSLog( @"test" );

} ];

// - test

[ object_ release ];




* This source code was highlighted with Source Code Highlighter .


Now we have everything we need to implement the primary task of the chapter - writing a method that generates a deferred call after a specified time, which does not cause a “retain” for a “target”. Implementation:

-( void )performSelector:( SEL )selector_

timeInterval:( NSTimeInterval )time_interval_

userInfo:( id )user_info_

repeats:( BOOL )repeats_

{

//

NSString* selector_string_ = NSStringFromSelector( selector_ );

NSUInteger num_of_args_ = [ selector_string_ numberOfCharacterFromString: @":" ];

NSString* assert_warning_ = [ NSString stringWithFormat: @"selector \"%@\" should has 0 or 1 parameters" , selector_string_ ];

NSAssert( num_of_args_ == 0 || num_of_args_ == 1, assert_warning_ );



// - __block self_ self

__block id self_ = self;



// scheduled ,

JFFScheduledBlock block_ = ^( JFFCancelScheduledBlock cancel_ )

{

//

if ( !repeats_ )

{

[ self_ removeOnDeallocBlock: cancel_ ];

cancel_();

}



//

num_of_args_ == 1

? objc_msgSend( self_, selector_, user_info_ )

: objc_msgSend( self_, selector_ );

};



JFFScheduler* scheduler_ = [ JFFScheduler sharedScheduler ];



//

JFFCancelScheduledBlock cancel_ = [ scheduler_ addBlock: block_

duration: time_interval_ ];

// scheduled self

[ self addOnDeallocBlock: cancel_ ];

}




* This source code was highlighted with Source Code Highlighter .


And of course, an example of using this method:

SomeClass* object_ = [ [ SomeClass alloc ] init ];



// print

[ object_ performSelector: @selector( print )

timeInterval: 1.

userInfo: nil

repeats: NO ];



// release print ,

// object_

[ object_ release ];



* This source code was highlighted with Source Code Highlighter .


Total. A similar method in the project that I am writing existed even before the appearance of the blocks, but its implementation contained exactly 2.5 times more lines of code and was fixed more than once. The implementation with the blocks was given to me the first time and so far no errors were found. I hope these examples were interesting for you.



Continued: "On blocks and their use in Objective-C part 3"

Source: https://habr.com/ru/post/120869/



All Articles