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]。