block的实质是什么?objective-c
查看block源码:编程
block定义:性能优化
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
复制代码
一共有几种block?bash
都是什么状况下生成的?微信
* [参考1:block 系列文章之一](https://www.jianshu.com/p/852eb39c0fd2)
* [参考2:又见block系列](https://blog.csdn.net/GeekLee609/article/details/82250664)(值得再看一遍)
* 参考3:《Objective-C高级编程 iOS与OS X多线程和内存管理》电子书参考第二章
复制代码
Block只捕获Block中会用到的变量。因为只捕获了自动变量(自动变量是以值传递方式传递到Block的构造函数里面)的值,并不是内存地址,因此Block内部不能改变自动变量的值。Block捕获的外部变量能够改变值的是静态变量,静态全局变量,全局变量。多线程
如何在block中修改外部自动变量的值,主要有如下2种方法:app
循环引用:框架
#import <Foundation/Foundation.h>
typedef void(^Study)();
@interface Student : NSObject
@property (copy , nonatomic) NSString *name;
@property (copy , nonatomic) Study study;
@end
#import "ViewController.h"
#import "Student.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *student = [[Student alloc]init];
student.name = @"Hello World";
student.study = ^{
NSLog(@"my name is = %@",student.name);
};
复制代码
block界面反向传值实例:
一、在第二个视图控制器的.h文件中定义声明Block属性:函数
// NextViewController.h
// 定义block
@property (nonatomic,copy) void (^NextViewControllerBlock)
(NSString *tfText);
// NextViewController.m
@interface NextViewController ()
@property (weak, nonatomic) IBOutlet UITextField *inputTF;
@end
- (IBAction)BtnAction:(id)sender {
//判断block是否为空
if (self.NextViewControllerBlock) {
self.NextViewControllerBlock(self.inputTF.text);
}
[self.navigationController popViewControllerAnimated:YES];
}
复制代码
二、在第一个视图中得到第二个视图控制器,而且用第二个视图控制器来调用定义的属性:性能
// AViewController.m
@interface AViewController ()
@property (weak, nonatomic) IBOutlet UILabel *nextVCInfoLabel;
@end
- (IBAction)btnClicked:(id)sender {
NextViewController *nextVC = [[NextViewController alloc]init];
nextVC.NextViewControllerBlock = ^(NSString *tfText){
self.nextVCInfoLabel.text = tfText;
};
[self.navigationController pushViewController:nextVC animated:YES];
}
复制代码
使用NSTimer可能会碰到循环引用的问题。特别是当类具备NSTimer类型的成员变量,而且须要反复执行计时任务时。例如
_timer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(startCounting) userInfo:nil
repeats:YES];
复制代码
类有一个成员变量_timer,给_timer设置的target为这个类自己。这样类保留_timer,_timer又保留了这个类,就会出现循环引用的问题,最后致使类没法正确释放。
解决这个问题的方式也很简单,当类的使用者可以肯定不须要使用这个计时器时,就调用
[_timer invalidate];
_timer = nil;
复制代码
这样就打破了保留环,类也能够正确释放。可是,这种依赖于开发者手动调用方法,才能让内存正确释放的方式不是一个很是好的处理方式。因此须要另一种解决方案。
以下所示:
@interface NSTimer (JQUsingBlock)
+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
block:(void(^)())block
repeats:(BOOL)repeats;
@end
@implementation NSTimer (JQUsingBlock)
+ (NSTimer *)jq_scheduledTimerWithTimeInterval:(NSTimeInterval)ti
block:(void(^)())block
repeats:(BOOL)repeats{
return [self scheduledTimerWithTimeInterval:ti
target:self selector:@selector(jq_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)jq_blockInvoke:(NSTimer *)timer{
void(^block)() = timer.userInfo;
if (block) {
block();
}
}
@end
复制代码
定义一个NSTimer的类别,在类别中定义一个类方法。类方法有一个类型为块的参数(定义的块位于栈上,为了防止块被释放,须要调用copy方法,将块移到堆上)。
使用这个类别的方式以下:
__weak ViewController *weakSelf = self;
_timer = [NSTimer jq_scheduledTimerWithTimeInterval:5.0
block:^{
__strong ViewController *strongSelf = weakSelf;
[strongSelf startCounting];
}
repeats:YES];
复制代码
使用这种方案就能够防止NSTimer对类的保留,从而打破了循环引用的产生。__strong ViewController *strongSelf = weakSelf主要是为了防止执行块的代码时,类被释放了。在类的dealloc方法中,记得调用[_timer invalidate]。