導(dǎo)語(yǔ)
block是一些簡(jiǎn)短代碼片段的封裝.本質(zhì)是函數(shù)指針. 雖然block聲明的時(shí)候不帶星,而且默認(rèn)情況下存放在棧里,但是,他確實(shí)是OC對(duì)象,往下看會(huì)有示例說(shuō)明
block類(lèi)型
- _NSConcreteGlobalBlock 全局的靜態(tài) block,不會(huì)訪問(wèn)任何外部變量
- _NSConcreteStackBlock 保存在棧中的 block,當(dāng)函數(shù)返回時(shí)會(huì)被銷(xiāo)毀。但在ARC下,會(huì)被復(fù)制到堆上.
- _NSConcreteMallocBlock 保存在堆中的 block,當(dāng)引用計(jì)數(shù)為 0 時(shí)會(huì)被銷(xiāo)毀。 所以在ARC下,只有_NSConcreteGlobalBlock和_NSConcreteMallocBlock這兩種類(lèi)型
寫(xiě)個(gè)例子來(lái)測(cè)試一下block類(lèi)型
/** 在.h中定義和.m中定義對(duì)結(jié)果沒(méi)有影響,已驗(yàn)證完畢*/
//.h中定義類(lèi)型,聲明屬性htestBlock
typedef NSString *(^testBlock)(NSString *);
@property (nonatomic ,copy)testBlock htestBlock;
//.m中聲明屬性mtestBlock及變量_ctestBlock
@property (nonatomic ,copy)testBlock mtestBlock; @implementation ViewController
{
testBlock _ctestBlock;
}
//下面進(jìn)行測(cè)試
self.htestBlock = ^(NSString *str)
{
return str;
};
self.mtestBlock = ^(NSString *str)
{
return str;
};
_ctestBlock = ^(NSString *str)
{
return str;
};
NSLog(@"%@",self.htestBlock(@"h"));
NSLog(@"%@",self.mtestBlock(@"m"));
NSLog(@"%@",_ctestBlock(@"c"));

結(jié)果如上圖,你會(huì)發(fā)現(xiàn),全部都是GlobalBlock額,但傳聞GlobalBlock不會(huì)訪問(wèn)任何外部變量,所以接著測(cè)試
NSString *name = @"daqianqian";
self.htestBlock = ^(NSString *str)
{
return name;
};
self.mtestBlock = ^(NSString *str)
{
return name;
};
_ctestBlock = ^(NSString *str)
{
return name;
};
NSLog(@"%@",self.htestBlock(@"h"));
NSLog(@"%@",self.mtestBlock(@"m"));
NSLog(@"%@",_ctestBlock(@"c"));

這下全部變成MallocBlock了
根據(jù)上面的結(jié)果,個(gè)人猜測(cè),block聲明后,就是全局的靜態(tài)block,因?yàn)楝F(xiàn)在使用的是ARC,所以當(dāng)你訪問(wèn)外部變量,會(huì)自動(dòng)copy一份到堆上.
若理解不對(duì)請(qǐng)大家指正,感謝!
__block
再做個(gè)小測(cè)試
//聲明block及屬性myBlock
typedef NSString * (^Block) (int);
@property(nonatomic, copy)Block myBlock;

你想在在內(nèi)部更改外部變量,你會(huì)發(fā)現(xiàn),無(wú)法更改!
因?yàn)閎lock如果要訪問(wèn)外部變量,他會(huì)拷貝進(jìn)來(lái)一份外部變量,并且這個(gè)外部變量是只讀的
外部變量改變也并不影響block內(nèi)部拷貝的那一份變量
請(qǐng)接著看下面的例子

這個(gè)輸出結(jié)果如下,驗(yàn)證了上面的判斷

如果不想讓block拷貝變量,而是想讓內(nèi)部使用的變量和外部使用的變量指向同一地址的話,需要在變量前面加上__block關(guān)鍵字,則外部變量不再是只讀的,在block內(nèi)部也可以改變它的值

定義和使用block
- 獨(dú)立block,作為類(lèi)的變量或?qū)傩允褂?br>
1- 實(shí)現(xiàn)界面反向傳值
2- 可以實(shí)現(xiàn)界面A做完某一操作后,界面B立即實(shí)現(xiàn)其他操作 - 內(nèi)聯(lián)block,作為方法的參數(shù)使用
1-配合disptch_queue,可以方便的實(shí)現(xiàn)簡(jiǎn)單的多線程編程和異步編程
Demo1 - 使用獨(dú)立block,實(shí)現(xiàn)界面反向傳值
獨(dú)立block格式:
typedef 返回值類(lèi)型 (^block名稱)(參數(shù)列表)
/** 在界面2聲明block,*/
//自定義格式
typedef void(^CCBlock)(NSString *);
//聲明屬性
@property (nonatomic, copy)CCBlock myCCBlock;
@implementation TwoViewController
{
//聲明變量
CCBlock myCCBlock2;
}
//在你需要的地方,寫(xiě)好調(diào)用方法
if(self.myCCBlock)
{
self.myCCBlock(@"你好");
}
/** 在界面1聲明界面2的對(duì)象,并實(shí)現(xiàn)回調(diào)*/
TwoViewController *secondVC = [[TwoViewController alloc] init];
secondController.myCCBlock = ^(NSString *str)
{
//在此處你可取到傳來(lái)的"你好",可以做刷新界面等其他操作
};
就這樣,輕松實(shí)現(xiàn)了界面的反向傳值
接上面的Demo1,再做個(gè)小測(cè)驗(yàn)
在界面2聲明一個(gè)數(shù)組,我們?cè)诮缑?初始化的時(shí)候就調(diào)用block,并將值作為對(duì)象存在數(shù)組里,打印結(jié)果如下:
self.myCCBlock(@"lalala");
NSArray *array = [[NSArray alloc] initWithObjects:@"1",self.myCCBlock,@"2",nil];
NSLog(@"myCCBlock內(nèi)容為%@,數(shù)組內(nèi)容為為%@",self.myCCBlock,array);

這說(shuō)明,block就是OC對(duì)象,因?yàn)樗艽嬖跀?shù)組里!
Demo2 - 使用內(nèi)聯(lián)block,實(shí)現(xiàn)
內(nèi)聯(lián)block格式:
(返回值 (^)(參數(shù)列表))此方法的參數(shù)名
/** 在界面2聲明內(nèi)聯(lián)block并調(diào)用*/
- (void)insideBlock: (NSString *)name successBlock:(void (^)(NSString *))success faileBlock:(void (^)(NSString *))fails;
- (void)insideBlock: (NSString *)name successBlock:(void (^)(NSString *))success faileBlock:(void (^)(NSString *))fails
{
//可以在這里做判斷,比如有一個(gè)BOOL值控制,為T(mén)rue就調(diào)用success(name),為False就調(diào)用faile(name)
success(name);
faile(name);
}
/** 在界面1聲明界面2對(duì)象,并實(shí)現(xiàn)回調(diào)*/
TwoViewController *secondController = [[TwoViewController alloc] init];
[secondController insideBlock:@"name" successBlock:^(NSString *response)
{
NSLog(@"內(nèi)聯(lián)函數(shù)成功的回調(diào)是%@",response);
}
faileBlock:^(NSString *response)
{
NSLog(@"內(nèi)聯(lián)函數(shù)失敗的回調(diào)是%@",response);
}];
就這樣,我們實(shí)現(xiàn)了將一段程序塊寫(xiě)在函數(shù)里面
循環(huán)引用
還是做個(gè)測(cè)驗(yàn)來(lái)分享
大家應(yīng)該知道,navigationController在pop掉界面的時(shí)候,這個(gè)界面會(huì)被銷(xiāo)毀,在界面上加上這句話,會(huì)發(fā)現(xiàn)pop時(shí)這句話會(huì)打印出來(lái)
- (void)dealloc
{
NSLog(@"被銷(xiāo)毀了");
}
若出現(xiàn)循環(huán)引用,會(huì)出現(xiàn)什么情況呢,就是A引用B,B引用A,引用計(jì)數(shù)都為2,調(diào)用dealloc的時(shí)候釋放一次,但結(jié)果是AB的引用計(jì)數(shù)仍為1,無(wú)法銷(xiāo)毀,占用內(nèi)存.
/** 在界面2.h聲明block類(lèi)型及屬性,另外聲明一個(gè)屬性name*/
typedef void (^Block) (NSString *);
@property(nonatomic, copy)Block myBlock;
@property(nonatomic, strong)NSString *name;
/** 在界面2.m給屬性name賦值,并在點(diǎn)擊按鈕返回界面1時(shí),調(diào)用block,注意此時(shí)沒(méi)傳值*/
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"lele";
}
- (IBAction)onClick:(id)sender
{
if (self.myBlock)
{
self.myBlock(@"");
}
[self .navigationController popViewControllerAnimated:NO];
}
/** 在界面1聲明界面2對(duì)象,并在界面1實(shí)現(xiàn)"在界面2點(diǎn)擊按鈕調(diào)用block方法"后的回調(diào)*/
- (IBAction)onClick:(id)sender
{
TwoViewController *secondVC = [[TwoViewController alloc] init];
secondVC.myBlock = ^(NSString * str)
{
NSLog(@"%@",secondVC.name);
};
[self.navigationController pushViewController:secondVC animated:NO];
}
此時(shí)運(yùn)行程序你會(huì)發(fā)現(xiàn),點(diǎn)擊界面2的按鈕后,界面2pop掉,返回界面1,"le'le"被傳了過(guò)來(lái),但卻沒(méi)有執(zhí)行delloc,沒(méi)有打印"被銷(xiāo)毀了"

原因
1- 在界面1中,執(zhí)行此句,VC的引用計(jì)數(shù)為1
TwoViewController *secondVC = [[TwoViewController alloc] init];
2- 在界面1中,執(zhí)行此句,block的引用計(jì)數(shù)為1
secondVC.myBlock = ^(NSString * str)
{
};
3- 在界面1中,執(zhí)行此句,VC的引用計(jì)數(shù)為2
NSLog(@"%@",secondVC.name);
4- 在界面2中,執(zhí)行此句,block的引用計(jì)數(shù)為2
self.myBlock(@"");
5- 界面pop時(shí),引用計(jì)數(shù)只會(huì)減1,所以造成循環(huán)引用
解決辦法
手動(dòng)將VC改為弱引用,使用"__weak typeof(原來(lái)VC類(lèi)名) 自己再起個(gè)名字"方法.
- (IBAction)onClick:(id)sender
{
TwoViewController *secondVC = [[TwoViewController alloc] init];
__weak typeof(secondVC) vc;
secondVC.myBlock = ^(NSString * str)
{
NSLog(@"%@",vc.name);
};
[self.navigationController pushViewController:secondVC animated:NO];
}
打印結(jié)果如圖,因?yàn)楸讳N(xiāo)毀了,所以取不到他的屬性值name

補(bǔ)充
-
1-內(nèi)聯(lián)block普通
(返回值 (^)(參數(shù)列表))此方法的參數(shù)名
/** 在界面2聲明內(nèi)聯(lián)block并調(diào)用*/
- (void)insideBlock: (NSString *)name successBlock:(void (^)(NSString *))success faileBlock:(void (^)(NSString *))fails;
- (void)insideBlock: (NSString *)name successBlock:(void (^)(NSString *))success faileBlock:(void (^)(NSString *))fails
{
//可以在這里做判斷,比如有一個(gè)BOOL值控制,為T(mén)rue就調(diào)用success(name),為False就調(diào)用faile(name)
success(name);
faile(name);
}
/** 在界面1聲明界面2對(duì)象,并實(shí)現(xiàn)回調(diào)*/
TwoViewController *secondController = [[TwoViewController alloc] init];
[secondController insideBlock:@"name" successBlock:^(NSString *response)
{
NSLog(@"內(nèi)聯(lián)函數(shù)成功的回調(diào)是%@",response);
}
faileBlock:^(NSString *response)
{
NSLog(@"內(nèi)聯(lián)函數(shù)失敗的回調(diào)是%@",response);
}];
1.系統(tǒng)會(huì)先調(diào)用
TwoViewController *secondController = [[TwoViewController alloc] init];
2.然后調(diào)用
[secondController insideBlock:@"name"
successBlock:^(NSString *response)
3.走到這里去調(diào)用這個(gè)方法內(nèi)部
- (void)insideBlock: (NSString *)name
successBlock:(void (^)(NSString *))success
faileBlock:(void (^)(NSString *))fails
{
success(name);
fails(name);
}
如果這里有success(name);則去回調(diào)下圖中的第一個(gè)方框
如果這里有fails(name);則去回調(diào)下圖中的第二個(gè)方框

-
2-內(nèi)聯(lián)block嵌套
本例子使用了FMDB,需導(dǎo)入<FMDB.h>
// 1.block類(lèi)_初始化
#import "TwoViewController.h"
#import "objc/runtime.h"
#import <FMDB.h>
static TwoViewController *_vcSingleton = nil;
@interface TwoViewController ()
{
FMDatabase *_db;
}
@end
@implementation TwoViewController
#pragma mark - 單例
+(instancetype)sharedDBSingleton
{
if (_vcSingleton == nil)
{
_vcSingleton = [[TwoViewController alloc] init];
[_vcSingleton initVCSingleton];
}
return _vcSingleton;
}
- (void)initVCSingleton
{
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"model.sqlite"];
NSLog(@"地址是%@",filePath);
_db = [FMDatabase databaseWithPath:filePath];
NSLog(@"這個(gè)db是什么%@",_db);
[_db open];
// 初始化數(shù)據(jù)表
NSString *personSql = @"CREATE TABLE 'animal' ('animal_name' VARCHAR(255))";
[_db executeUpdate:personSql];
[_db close];
}
// 2.block類(lèi)_block
#pragma mark - Block
- (void)insideBlock1: (NSString *)name successBlock:(BOOL (^)(FMDatabase *db,SInt32 lastVersion))success
{
SInt32 lastVersion = 5555;
[self inTransaction:^(FMDatabase *db, BOOL *shouldRollback)
{
if (success(db, lastVersion))
{
NSLog(@"這是什么");
}
else
{
NSLog(@"這又是什么");
}
}];
}
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *shouldRollback))block
{
BOOL shouldRollback = NO;
block(_db, &shouldRollback);
}
//3.其他類(lèi)_調(diào)用block
TwoViewController *secondController = [TwoViewController sharedDBSingleton];
[secondController insideBlock1:@"test" successBlock:
^BOOL(FMDatabase *db, SInt32 lastVersion)
{
NSLog(@"傳來(lái)的db是什么%@,傳來(lái)的lastVersion是什么%d",db,(int)lastVersion);
return YES;
}];
-
1.先執(zhí)行
TwoViewController *secondController = [TwoViewController sharedDBSingleton];
在initVCSingleton中初始化了db為<FMDatabase: 0x60000008e920> -
2.調(diào)用
[secondController insideBlock1:@"test" successBlock:
^BOOL(FMDatabase *db, SInt32 lastVersion)
的時(shí)候,是在block類(lèi)內(nèi)部調(diào)用,先執(zhí)行
SInt32 lastVersion = 5555; -
3.然后執(zhí)行了
[self inTransaction:^(FMDatabase *db, BOOL *shouldRollback)
方法內(nèi)部,因?yàn)閎lock(_db, &shouldRollback);
所以執(zhí)行的是下面的回調(diào),傳進(jìn)來(lái)的兩個(gè)參數(shù)就是_db和shouldRollback = NO

下面的這個(gè)判斷,就會(huì)去其他類(lèi)_調(diào)用block中調(diào)用,并傳過(guò)去_db和5555

可以看到下圖是返回YES

所以執(zhí)行下面這段
