Slide 1

Slide 1 text

Fun with blocks in ObjC Cyril @notorca Lashkevich

Slide 2

Slide 2 text

const char *s = "abc" "cde"; ! NSString *ns1 = @"abc" @"cde"; ! NSString *ns2 = @"abc" "cde"; ! NSString *ns1 = "abc" @"cde";

Slide 3

Slide 3 text

History First appeared as ObjC/C/C++ extension in iOS 4 and Mac OS 10.6 Was implemented in gcc 4.2 (with bugs) and clang 3.1 First blocks-oriented API is the Grand Central Dispatch iOS 5 ARC (changed __block semantics) iOS 7 no need to call copy for blocks in many cases

Slide 4

Slide 4 text

Similar concepts First class functions GCC nested functuons extension C++11 lambdas Ruby blocks Proc’s and lambdas Python lambdas Lua/JavaScript functions

Slide 5

Slide 5 text

Syntax As a local variable: returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; As a property: @property (nonatomic, copy) returnType (^blockName)(parameterTypes); As a method parameter: - (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName; As an argument to a method call: [someObject someMethodThatTakesABlock: ^returnType (parameters) {...}]; As a typedef: typedef returnType (^TypeName)(parameterTypes); TypeName blockName = ^returnType(parameters) {...}; http://fuckingblocksyntax.com

Slide 6

Slide 6 text

Syntax In the implementation return type is optional and can be inferred by compiler:
 ^(void) { if(something) return 42; else return 43; } No parameters, parentheses are optional:
 ^ { if(something) return 42; else return 43; } Type can be deduced with typeof instruction:
 typeof(^ { if(something) return 42; else return 43; })

Slide 7

Slide 7 text

Blocks as first class citizens Blocks are special kind of objects compatible with id Have *isa as first member Managed with retain/release/copy Can be passed as parameters and retuned from functions and methods Can be stored in containers Can be used in C++ algorithms as functors

Slide 8

Slide 8 text

vector transfers; ... NSMutableArray *transferHist = 
 [[NSMutableArray alloc]
 initWithCapacity:transfers.size()]; for_each(transfers.begin(), transfers.begin(), ^(const int &x) { [transferHist addObject:@(x)]; }) NSDictionary *commands = @{ @"foo" : ^{ [self foo]; }, @"bar" : ^{ [self bar]; } };

Slide 9

Slide 9 text

Blocks are closures They can capture values form lexical scope and prolong their lifetime By default values are captured as const, pointer to objects are referenced so using of self in block creates retain cycle if block somehow stored in the object __block changes semantics of capturing (different with and without ARC)

Slide 10

Slide 10 text

Types of blocks NSGlobalBlock NSStackBlock NSMallocBlock

Slide 11

Slide 11 text

Block from stack to heap .text invoke() Locals Stack *isa *invoke Scope locals NSStackBlock NSMallocBlock __block Locals *isa *invoke Scope locals

Slide 12

Slide 12 text

Reference to self self is captured by strong reference in block Access to ivar is done throw implicit self
 ^ { NSLog(@"%@", _ivar); };
 ^ { NSLog(@"%@", self->_ivar); }; Retain cycle when block is saved as class member

Slide 13

Slide 13 text

__weak typeof(self) weakSelf = self; [obj doWithBlock:^ { [weakSelf doThing]; [weakSelf doOtherThing]; }]; __weak typeof(self) weakSelf = self; [obj doWithBlock:^ { __strong typeof(self) strongSelf = weakSelf; [strongSelf doThing]; [strongSelf doOtherThing]; }]; self still can be used and create retain cycle (for example when using RACObserve)

Slide 14

Slide 14 text

@weakify(self); [target.rac_deallocDisposable addDisposable: [RACDisposable disposableWithBlock:^{ @strongify(self); [self.leadingTerminal sendCompleted]; self.target = nil; }]]; @strongify(self) can be missed @weakify(self) can be missed direct access to ivar -> retain cycle

Slide 15

Slide 15 text

Goals Safe using of self in the block: weak until the block is called, strong during call self should called self Checking for ivar access in the block 
 [RACObserve(self, pttState) subscribeNext:@weakselfnotnil(^(NSNumber *state)) { self.isRecordingPTT = !!state.intValue; } @weakselfend];

Slide 16

Slide 16 text

Block can be split arguments part:
 ^(NSHTTPURLResponse *response, NSData *data, NSError *error) body:{ if (error) NSLog(@"Error: %@", error); else { self->statusCode = [response statusCode]; NSLog(@"HTTP status %d", statusCode); [self saveData:data forResponse:response]; } }

Slide 17

Slide 17 text

#define weakself(ARGS, BODY) weakself(^(NSHTTPURLResponse *response, NSData *data, NSError *error), { … }) ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); }

Slide 18

Slide 18 text

^(NSHTTPURLResponse *response, NSData *data, NSError *error) { __strong typeof(weakSelf) self = weakSelf; return ^{ if (error) { NSLog(@"Error: %@", error); } else { self->statusCode = [response statusCode]; NSLog(@"HTTP status %d", statusCode); [self saveData:data forResponse:response]; } } (); } ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); }

Slide 19

Slide 19 text

^(__weak typeof(self) weakSelf) { return ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); } } (self) ^(__weak typeof(self) weakSelf) { return ^(NSHTTPURLResponse *response, NSData *data, NSError *error) { __strong typeof(weakSelf) self = weakSelf; return ^{ if (error) { NSLog(@"Error: %@", error); } else { self->statusCode = [response statusCode]; NSLog(@"HTTP status %d", statusCode); [self saveData:data forResponse:response]; } } (); } (self)

Slide 20

Slide 20 text

Too much blocks! Code Block Evaluation C Extension

Slide 21

Slide 21 text

self.searchBar = ({ UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:({ CGRect frame = self.tableView.frame; frame.size.height = 50.0f; frame; })]; searchBar.delegate = self; searchBar; }); Example from NSHipster

Slide 22

Slide 22 text

#define weakself(ARGS, BODY) ({ __weak typeof(self) weakSelf = self; ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); }; }) #define weakself(ARGS, BODY) ^(__weak typeof(self) weakSelf) { return ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); } } (self)

Slide 23

Slide 23 text

Checking ivar access retainCount method! Unavailable in ARC CFGetRetainCount still works well

Slide 24

Slide 24 text

Compare reference count for self #define weakself(ARGS, BODY) ({ __weak typeof(self) weakSelf = self; ARGS { \ __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); }; }) Here And here

Slide 25

Slide 25 text

How to do something after return? @onExit form libextobjc CFMutableDictionaryRef processedObjects = CFDictionaryCreateMutable(NULL, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks); if (processedObjects == NULL) return nil; @onExit { CFRelease(processedObjects); };

Slide 26

Slide 26 text

({ __weak typeof(self) weakSelf = self; NSUInteger refBefore = CFGetRetainCount((__bridge CFTypeRef)self); @onExit { NSUInteger refAfter = CFGetRetainCount((__bridge CFTypeRef)weakSelf); assert(refBefore == refAfter); } ARGS { __strong typeof(weakSelf) self = weakSelf; return ^ BODY (); }; })

Slide 27

Slide 27 text

Used extensions Code block evaluation ({ … }) typeof(…) __attribute((cleanup(…)))

Slide 28

Slide 28 text

struct RefCountCheckerData { CFTypeRef weakSelf; NSUInteger refCountBefore; }; ! static inline void vbr_CheckRefCountForWeakSelf(struct RefCountCheckerData *data) { const NSUInteger refCountAfter = CFGetRetainCount(data->weakSelf); const NSUInteger countOfSelfRefInBlock = refCountAfter - data->refCountBefore; assert(countOfSelfRefInBlock == 0); } ! #define weakself(ARGS) \ "weakself should be called as @weakself" @"" ? \ ({ __weak typeof(self) _private_weakSelf = self; \ __attribute__((cleanup(vbr_CheckRefCountForWeakSelf), unused)) \ struct RefCountCheckerData _private_refCountCheckerData = { \ .weakSelf = (__bridge CFTypeRef)self, \ .refCountBefore = CFGetRetainCount((__bridge CFTypeRef)self), \ };\ ARGS { \ __strong typeof(_private_weakSelf) self __attribute__((unused)) = \ _private_weakSelf; \ return ^ (void) { ! #define weakselfend \ try {} @finally {} } (); }; \ }) : nil

Slide 29

Slide 29 text

https://gist.github.com/ notorca/9192459

Slide 30

Slide 30 text

Bonus
 Cool macro with @ #define weakify(...) \ try {} @finally {} \ metamacro_foreach_cxt(mtl_weakify_,, __weak, __VA_ARGS__) #define weakify(...) \ autoreleasepool {} \ metamacro_foreach_cxt(mtl_weakify_,, __weak, __VA_ARGS__)

Slide 31

Slide 31 text

#define coolmacro(ARGS) \ 1 ? ({ ... }) : nil ! @coolmacro(111) -> @1 ? ({ ... }) : nil coolmacro(111) -> 1 ? ({ ... }) : nil #define coolmacro(ARGS) \ [@1] ? ({ ... }) : nil ! @coolmacro(111) -> @[@1] ? ({ ... }) : nil coolmacro(111) -> [@1] ? ({ ... }) : nil

Slide 32

Slide 32 text

const char *s = "abc" "cde"; ! NSString *ns1 = @"abc" @"cde"; ! NSString *ns2 = @"abc" "cde"; ! NSString *ns1 = "abc" @"cde";

Slide 33

Slide 33 text

#define coolmacro(ARGS) \ "@ missed" @"" ? ({ ... }) : nil ! @coolmacro(111) -> @"@ missed" @"" ? ({ ... }) : nil coolmacro(111) -> "@ missed" @"" ? ({ ... }) : nil

Slide 34

Slide 34 text

@notorca