티스토리 뷰

GCD is the associated technology and provides an abstraction to threading, based on so-called dispatch queues. Blocks can be enqueued on these queues, and GCD handles all scheduling for you. It creates, reuses, and destroys background threads as it sees fit, based on system resources, to process each queue. Moreover, GCD provides easy-to-use solutions to common programming tasks, such as thread-safe single-code execution and running the tasks in parallel, based on available system resources.

 

Item 37: Understand Blocks

int additional = 5;
int (^addBlock)(int a, int b) = ^(int a, int b){
    return a + b + additional;
};
int add = addBlock(2, 5); //< add = 12

By default, any variable captured by a block cannot be modified by the block. In the example, if the variable called additional were changed within the block, the compiler would issue an error. 

 

void (^block)();
if ( /* some condition */ ) {
    block = ^{
        NSLog(@"Block A");
    };
} else {
    block = ^{
        NSLog(@"Block B");
    };
}
block();

The two blocks that are defined within the if and else statements are allocated within stack memory. When it allocates stack memory for each block, the compiler is free to overwrite this memory at the end of the scope in which that memory was allocated. So each block is guaranteed to be valid only within its respective if-statement section. The code would compile without error but at runtime may or may not function correctly. If it didn’t decide to produce code that overwrote the chosen block, the code would run without error, but if it did, a crash would certainly occur.

To solve this problem, blocks can be copied by sending the block the copy message. In doing so, the block is copied from the stack to the heap.

void (^block)();
if ( /* some condition */ ) {
    block = [^{
        NSLog(@"Block A");
    } copy];
} else {
    block = [^{
        NSLog(@"Block B");
    } copy];
}
block();

This code is now safe.

 

Item 38: Create typedefs for Common Block Types

typedef int(^EOCSomeBlock)(BOOL flag, int value);

Just as the variable name of a block is in the middle prefixed by a caret, so too is the new type name.

EOCSomeBlock block = ^(BOOL flag, int value){
    // Implementation
};

 

typedef void(^ACAccountStoreSaveCompletionHandler)
                                (BOOL success, NSError *error);
typedef void(^ACAccountStoreRequestAccessCompletionHandler)
                                (BOOL granted, NSError *error);

These block type definitions have the same signature but are used in distinct places. The name of the type and the name of the parameters in the signature make it easy for the developer to understand how the type is to be used. The developer could have defined a single type definition, perhaps called ACAccountStoreBooleanCompletionHandler, used in place of both of these. However, that would lose the clarity of how the block and parameters are used.

 

Item 39: Use Handler Blocks to Reduce Code Separation

EOCNetworkFetcher *fetcher =
    [[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHander:
    ^(NSData *data, NSError *error){
        if (error) {
            // Handle failure
        } else {
            // Handle success
        }
    }];

This approach requires the error variable to be checked and puts all the logic in one place. The downside is that because all the logic is in one place, the block can become long and complicated. However, the upside of the single-block approach is that it is much more flexible. It’s possible to pass an error, as well as data, for example. Consider that perhaps the network was able to download half the data and then an error occurred. Maybe in that case, you would pass back the data and the associated error. The completion handler can then determine the problem, handle it as appropriate, and may be able to do something useful with the part of the data that was successfully downloaded

 

Item 40: Avoid Retain Cycles Introduced by Blocks Referencing the Object Owning Them

call back block = completion handler

 

Item 41: Prefer Dispatch Queues to Locks for Synchronization

 

Item 42: Prefer GCD to performSelector and Friends

- (id)performSelector:(SEL)selector

This is equivalent to calling the selector directly. So the following two lines of code are equivalent:

[object performSelector:@selector(selectorName)];
[object selectorName];

It might seem as though this is redundant. It would be if this were the only way the method could be used. However, its real power comes from the fact that the selector can be decided at runtime. Like:

SEL selector;
if ( /* some condition */ ) {
    selector = @selector(foo);
} else if ( /* some other condition */ ) {
    selector = @selector(bar);
} else {
    selector = @selector(baz);
}
[object performSelector:selector];

However, the result might be a memory leak.

 

For example, to perform a task after a delay, you should prefer the latter to the former:

// Using performSelector:withObject:afterDelay:
[self performSelector:@selector(doSomething)
           withObject:nil
           afterDelay:5.0];

// Using dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,
                                (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
    [self doSomething];
});

  

 The performSelector family of methods is potentially dangerous with respect to memory management. If it has no way of determining what selector is going to be performed, the ARC compiler cannot insert the appropriate memory-management calls.

 The family of methods is very limited with respect to the return type and the number of parameters that can be sent to the method.

 The methods that allow performing a selector on a different thread are better replaced with certain Grand Central Dispatch (GCD) calls using blocks.

 

Item 43: Know When to Use GCD and When to Use Operation Queues

- Cancelling operations

- Operation dependencies

- Key-Value Observing of operation properties

- Operation priorities

- Reuse of operations

 

Item 44: Use Dispatch Groups to Take Advantage of Platform Scaling

 

Item 45: Use dispatch_once for Thread-Safe Single-Time Code Excution

 

Item 46: Avoid dispatch_get_current_queue

cf. UI work in both Mac OS X and iOS needs to be performed on the main thread, which equates to the main queue in GCD.

 

 The dispatch_get_current_queue function does not in general perform how you would expect. It has been deprecated and should now be used only for debugging.

 


https://www.oreilly.com/library/view/effective-objective-c-20/9780133386950/

 

Effective Objective-C 2.0: 52 Specific Ways to Improve Your iOS and OS X Programs

Write Truly Great iOS and OS X Code with Objective-C 2.0! Effective Objective-C 2.0 will help you harness all of Objective-C’s expressive power to write OS X or iOS code … - Selection from Effective Objective-C 2.0: 52 Specific Ways to Improve Your iOS

www.oreilly.com

 

이 책으로 공부 중이고, 기억하면 좋을 것 같은 부분을 발췌 + 공부 + 정리합니다.

'Objective-C 2.0' 카테고리의 다른 글

7. The System Frameworks  (0) 2023.01.03
5. Memory Management  (0) 2022.05.10
4. Protocols and Categories  (0) 2022.03.04
3. Interface and API Design  (0) 2022.01.25
2. Objects, Messaging, and the Runtime  (0) 2022.01.19
댓글