[TOC]
This article mainly introduces 1. Basic types of blocks 2. Common problems and solutions: Circular reference 3. Frequently seen exam
The classification of the block
Three: heap on stack global
int a = 10;
void (^block)(void) = ^{
};
NSLog(@ "% @",block);//<__NSGlobalBlock__: 0x1015f9100>
int a = 10;
void (^block)(void) = ^ {NSLog(@"lr - %d",a);
};
NSLog(@ "% @",block);//<__NSMallocBlock__: 0x60000387c0f0>
int a = 10;
void (__weak ^block)(void) = ^ {NSLog(@"lr - %d",a);
};
NSLog(@ "% @",block);//<__NSStackBlock__: 0x7ffee1c29428>
Copy the code
GlobalBlock
- Located in global area
- External variables are not applied inside blocks, or only static and global variables are used
MallocBlock
- Located in the heap area
- Use local variables or OC attributes inside blocks and assign values to strongly referenced or copy-modified variables
StackBlock
- Located in the stack area
- As with mallocblocks, local variables or OC attributes can be used internally. But you cannot assign to strongly referenced or copy-modified variables
Block copied to heap:
- Manually copy
- Block as the return value
- Modified by strong reference or Copy
- The system API contains usingBlock
Exercise 1. Block captures external variables – reference counting processing of external variables
#pragmaMark-block captures external variables - reference counting processing of external variables
- (void)blockDemo2{
NSObject *objc = [NSObject new];
NSLog(@"%ld".CFGetRetainCount((__bridge CFTypeRef)(objc))); / / 1
// Block source code
// Capture + 1
/ block/heap area
// stack - memory -> heap + 1
void(^strongBlock)(void) = ^ {// 1 -block -> objc capture + 1 = 2
NSLog(@"---%ld".CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
strongBlock();
void(^ __weak weakBlock)(void) = ^ {/ / + 1
NSLog(@"---%ld".CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
weakBlock();
void(^mallocBlock)(void) = [weakBlock copy]; mallocBlock(); } the result1
--- 3
--4 -
--- 5
Copy the code
Block NSObject+ block. h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(*LGBlockCopyFunction)(void *, const void *);
typedef void(*LGBlockDisposeFunction)(const void *);
typedef void(*LGBlockInvokeFunction)(void*,...). ;enum {
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),};struct _LGBlockDescriptor1 {
uintptr_t reserved;
uintptr_t size;
};
struct _LGBlockDescriptor2 {
// requires BLOCK_HAS_COPY_DISPOSE
LGBlockCopyFunction copy;
LGBlockDisposeFunction dispose;
};
struct _LGBlockDescriptor3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
/ / the underlying
struct _LGBlock {
void *isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
// Function pointer
LGBlockInvokeFunction invoke;
struct _LGBlockDescriptor1 *descriptor;
};
static struct _LGBlockDescriptor3 * _LG_Block_descriptor_3(struct _LGBlock *aBlock) {
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return nil;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct _LGBlockDescriptor1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct _LGBlockDescriptor2);
}
return (struct _LGBlockDescriptor3 *)desc;
}
static const char *LGBlockTypeEncodeString(id blockObj) {
struct _LGBlock *block = (__bridge void *)blockObj;
return _LG_Block_descriptor_3(block)->signature;
}
@interface NSObject (Block)
// Prints the Block signature
- (NSMethodSignature *)getBlcokSignature;
// Prints the Block signature
- (NSString *)getBlcokSignatureString;
/ / call block
- (void)invokeBlock;
@end
NS_ASSUME_NONNULL_END
Copy the code
NSObject+Block.m
#import "NSObject+Block.h"
@implementation NSObject (Block)
- (NSString *)getBlcokSignatureString {
NSMethodSignature *signature = self.getBlcokSignature;
if (signature) {
NSMutableString *blockSignature = [NSMutableString stringWithFormat:@"BlcokSignature: return type: %s, ", [signature methodReturnType]];
for (int i = 0; i < signature.numberOfArguments; i++) {
[blockSignature appendFormat:@"argument number: %d, argument type: %s ", i+1, [signature getArgumentTypeAtIndex:i]];
}
return blockSignature;
}
return nil;
}
- (NSMethodSignature *)getBlcokSignature {
if ([self isKindOfClass:NSClassFromString(@"__NSMallocBlock__")] || [self isKindOfClass:NSClassFromString(@"__NSStackBlock__")] || [self isKindOfClass:NSClassFromString(@"__NSGlobalBlock__")]) {
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:LGBlockTypeEncodeString(self)];
return signature;
}
return nil;
}
- (void)invokeBlock {
NSMethodSignature *signature = self.getBlcokSignature;
if (signature) {
// Dynamic message forwarding
NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:signature];
[blockInvocation invokeWithTarget:self]; }}// block the OC object
//- (NSString *)description {
// if ([self isKindOfClass:NSClassFromString(@"__NSMallocBlock__")] || [self isKindOfClass:NSClassFromString(@"__NSStackBlock__")] || [self isKindOfClass:NSClassFromString(@"__NSGlobalBlock__")]) {
/ / / / signature
// return [NSString stringWithFormat:@"<%@:%p>--%@", self.class, self, [self getBlcokSignatureString]];
/ /}
// return [NSString stringWithFormat:@"<%@:%p>", self.class, self];
/ /}
@end
Copy the code
The sample
- (void)blockDemo1{
int a = 0;
void(^ __weak weakBlock)(void) = ^ {NSLog(@"-----%d", a);
};
struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
// Depth copy
id __strong strongBlock = weakBlock;
blc->invoke = nil;
void(^strongBlock1)(void) = strongBlock;// Use strongBlock to execute strongBlock1()
strongBlock1();
}
Copy the code
id __strong strongBlock = weakBlock; blc->invoke = nil
StrongBlock and BLC both point to the block on weakBlock stack and operate on the same piece of memory space. BLC -> Invoke = nil will affect the call of strongBlock behind, resulting in crash
- (void)blockDemo1{
int a = 0;
void(^ __weak weakBlock)(void) = ^ {NSLog(@"-----%d", a);
};
struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
// Depth copy
id __strong strongBlock = [weakBlock copy];
blc->invoke = nil;
void(^strongBlock1)(void) = strongBlock;// Use strongBlock to execute strongBlock1()
strongBlock1();
// Warning what??
}
Copy the code
id __strong strongBlock = [weakBlock copy]; WeakBlock copy can solve the crash problem, and the newly copied Block on the heap and the BLC on the original stack do not affect each other
3. Block stack releases the difference
- (void)blockDemo3{
// Stack memory translation
int a = 0;
void(^ __weak weakBlock)(void) = nil;
{
/ / the stack area
void(^ __weak strongBlock)(void) = ^ {NSLog(@"---%d", a);
};
weakBlock = strongBlock;
NSLog(@"1 - %@ - %@",weakBlock,strongBlock); } weakBlock(); } the result1 - <__NSStackBlock__: 0x7ffeef5543a0> - <__NSStackBlock__: 0x7ffeef5543a0>
--0
Copy the code
Void (^__weak strongBlock)(void) = ^{void(^__weak strongBlock)(void) = ^{
- (void)blockDemo3{
int a = 0;
void(^ __weak weakBlock)(void) = nil;
{
/ / heap area
void(^ strongBlock)(void) = ^ {NSLog(@"---%d", a);
};
weakBlock = strongBlock;
NSLog(@"1 - %@ - %@",weakBlock,strongBlock);//1 - <__NSMallocBlock__: 0x6000007dadf0> - <__NSMallocBlock__: 0x6000007dadf0>
}
weakBlock();
}
1 - <__NSMallocBlock__: 0x6000007dadf0> - <__NSMallocBlock__: 0x6000007dadf0>
(lldb)
Copy the code
Blocks in the heap are released when they are out of scope and an error is reported when they are called again
A circular reference to a block and its resolution
The most common problem with blocks is circular references. The figure above describes the process of circular references and common code examples of why circular references occur
static ViewController *staticSelf_;
typedef void(^LRBlock)(void);
@interface ViewController(a)
@property (nonatomic.strong) LRBlock block;
@property (nonatomic.copy) NSString *name;
@property (nonatomic.copy) LRBlock doWork;
@property (nonatomic.copy) LRBlock doStudent;
@property (nonatomic.strong) UITableView *tableView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Circular reference
[self block1];
}
- (void)block1{
__weak typeof(self) weakSelf = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@ "% @",weakSelf.name);
});
};
self.block();
}
# pragmaMark Weak
- (void)blockWeak_static {
// It is the same memory space
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
// staticSelf_ -> weakSelf -> self
}
# pragma mark weak_strong_dance
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@ "% @", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
- (void)dealloc{
NSLog(@ "dealloc is coming");
}
@end
Copy the code
Examples of code to generate a circular reference:
/ / code
self.block = ^(void) {NSLog(@ "% @".self.name);
};
self.block();
/ / code 2
[UIView animateWithDuration:1 animations:^{
NSLog(@ "% @".self.name);
}];
Copy the code
As soon as the code generates a circular reference, it uses the external variable name inside the block. Name is held by self, so blcok holds self and self already holds the block, so self -> block, block -> self.name -> self
There is no circular reference in code 2. Although the external variable name is used inside the block, the block is not held by self. Only block -> self.name -> self does not constitute a circular reference
Break block to self strong reference principle: solve self scope and block scope communication, communication methods have proxy, transmission value, notification, etc. Usually we have the following several ways to deal with
- Weak-strong-dance
- 2. Manually null, using a temporary variable (decorated with __block) referencing self, manually null inside the block
- 3. Pass the object self, which may generate a circular reference, as a block argument. Using arguments inside a block does not cause circular reference problems
- 4. NSProxy virtual class
Weak-strong-dance
1.1 General: __weak is used
- (void)block1{
__weak typeof(self) weakSelf = self;
self.block = ^(void) {NSLog(@ "% @",weakSelf.name);
};
self.block();
}
Copy the code
In general, we use __weak to modify self to remove the circular reference. Using __weak will not change the reference count of self. WeakSelf and self point to the same piece of memory space.
1.2 Block nesting: use both __weak and __strong
But there are cases (such as delayed operations) where clicking on VC and returning will call the above method and print (NULL) after 2s delay.
It can be found from printing that weakSelf has been released when printing delayed 2s
For this reason, __strong is usually used to create temporary variables to prevent weakSelf from being released early in the delay process
- (void)block1{
__weak typeof(self) weakSelf = self;
self.block = ^(void){
__strong __typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@ "% @",strongSelf.name);
});
};
self.block();
}
Copy the code
StrongSelf is a temporary variable, scoped inside the block, strongSelf will be released when the block execution is complete, so as to ensure weakSelf will not be released early and break the loop reference, depending on the mediator mode
2. Manually null, using a temporary variable (decorated with __block) referencing self, manually null inside the block
Depending on the mediator mode, vc needs to be released manually
- (void)block1{
// self -> block -> vc -> self
__block ViewController *vc = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@ "% @",vc.name);
vc = nil;
});
};
self.block();
}
Copy the code
3. Pass the object self, which may generate a circular reference, as a block argument. Using arguments inside a block does not cause circular reference problems
- (void)block1{
self.block = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@ "% @",vc.name);
});
};
self.block(self);
}
Copy the code
4. NSProxy virtual class
- OC can only have single inheritance, and since it runs real values, pseudo-multiple inheritance can be implemented through NSProxy
- NSProxy and NSObject is a childhood base class, or virtual class, that just implements the protocol of NSObject
- NSProxy is an abstract class that encapsulates message redirection. It is like a proxy, middleware, that can inherit it and rewrite the following two methods to implement message forwarding to another instance
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
Copy the code
There are two scenarios for using NSProxy
The realization of multiple inheritance function solves the problem of self strong reference when nstimer and CadisplayLink are created, refer to YYWeakProxy of YYKit.Copy the code
Use message forwarding to customize a NSProxy subclass by replacing self with a custom NSProxy class object
@interface LRProxy : NSProxy
- (id)transformObjc:(NSObject *)objc;
+ (instancetype)proxyWithObjc:(id)objc;
@end
@interface LRProxy(a)
@property (nonatomic.weak.readonly) NSObject *objc;
@end
@implementation LRProxy
- (id)transformObjc:(NSObject *)objc{
_objc = objc;
return self;
}
+ (instancetype)proxyWithObjc:(id)objc{
return [[self alloc] transformObjc:objc];
}
//2. The method implementation is called once the method signature is in place
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.objc respondsToSelector:sel]) {
[invocation invokeWithTarget:self.objc]; }}//1. Query the signature of the method
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
NSMethodSignature *signature;
if (self.objc) {
signature = [self.objc methodSignatureForSelector:sel];
}else{
signature = [super methodSignatureForSelector:sel];
}
return signature;
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.objc respondsToSelector:aSelector];
}
@end
Copy the code
Custom Cat and Dog classes
/ / * * * * * * * * the Cat class * * * * * * * *
@interface Cat : NSObject
@end
@implementation Cat
- (void)eat{
NSLog(@" Cats eat fish");
}
@end
/ / Dog class * * * * * * * * * * * * * * * *
@interface Dog : NSObject
@end
@implementation Dog
- (void)shut{
NSLog(@ "dog");
}
@end
Copy the code
Multiple inheritance example
- (void)proxyTest{
Dog *dog = [[Dog alloc] init];
Cat *cat = [[Cat alloc] init];
LRProxy *proxy = [LRProxy alloc];
[proxy transformObjc:cat];
[proxy performSelector:@selector(eat)];
[proxy transformObjc:dog];
[proxy performSelector:@selector(shut)];
}
Copy the code
LRProxy is used to solve the problem of timer self strong reference, solve the circular reference
self.timer = [NSTimer timerWithTimeInterval:1 target:[LRProxy proxyWithObjc:self] selector:@selector(print) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
- (void)dealloc{
[self.timer invalidate];
NSLog(@ "dealloc is coming");
}
Copy the code
Block loop quotes interview questions
# pragmaMark Weak
- (void)blockWeak_static {
// It is the same memory space
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
// staticSelf_ -> weakSelf -> self
}
# pragma mark weak_strong_dance
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@ "% @", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
Copy the code
StaticSelf_ -> weakSelf -> self Three variables point to the same memory space, which is the same memory space that self points to
Because staticSelf is static and has not been released out of scope after the VC returns, self’s reference count is not zero and cannot be released, resulting in a circular reference
WeakSelf = weakSelf reassigns a value. StaticSelf no longer points to self. The reference count of the original self is 0. The original cyclic reference is broken, but staticSelf and new VC after reassignment continue to generate cyclic reference 😏6️ 6️ 6️ one
For block_weak_strong, the problem itself will generate a circular reference solution. First, the strong and weak dance to the end. Each layer of block has its own strongSelf
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
__weak typeof(self) weakSelf = strongSelf;
weakSelf.doStudent = ^{
__strong typeof(self) strongSelf = weakSelf;
NSLog(@ "% @", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
Copy the code
Plan 2 Manually empty the switch
__block ViewController *vc = self;
self.doWork = ^{
vc.doStudent = ^{
NSLog(@ "% @", vc);
vc = nil;
};
vc.doStudent();
};
self.doWork();
Copy the code
Plan 3: Transfer parameters
@property (nonatomic.copy) void(^doWork1)(ViewController *vc);
@property (nonatomic.copy) void(^doStudent1)(ViewController *vc);
self.doWork1 = ^(ViewController *vc) {
vc.doStudent1 = ^(UIViewController *vc) {
NSLog(@ "% @", vc);
};
vc.doStudent1(vc);
};
self.doWork1(self);
Copy the code