iOS中block比較常用,但是又和OC的語法顯得有點格格不入,難于理解。
以下是我個人初步的理解,供查閱。
1.block的聲明
//聲明一個block
typedef NSString *(^WXYTestBlock)(NSString *name, int age);
以上聲明了一個名字叫做WXYTestBlock的block,參數為一個字符串類型的name和一個int類型的age,返回值為NSString。
當然,你也可以聲明成這樣:
typedef void (^WXYTestBlock)(void);
無參數,無返回值。
當然也可以有參數無返回值,或者有返回值無參數,不一一列舉。
2.block的使用
首先是獨立block
//獨立block? ?
WXYTestBlock myBlock = ^ (NSString *name, int age){? ? ?
? ? return [NSString stringWithFormat:@"%@的年齡是%d",name,age];?
};? ?
NSLog(@"獨立block--->%@", myBlock(@"小宇", 16));
獨立block可以直接定義和使用,運行輸出如下
2015-06-03 23:32:32.532 WXYBlock[3537:237755] 獨立block--->小宇的年齡是16
然后是內聯(lián)block
//使用內聯(lián)block的方法
- (void)printWithName:(NSString *)name age:(int)age block:(WXYTestBlock)block{? ? NSLog(@"內聯(lián)block--->%@",block(name, age));
}
內聯(lián)block需要將定義的block作為參數傳入相應的方法中,然后在方法中使用block。
//內聯(lián)block?
[self printWithName:@"王興宇" age:26 block:^(NSString *str, int age){? ? ?
? ? return [NSString stringWithFormat:@"%@的年齡是%d", str, age];?
}];
內聯(lián)block可以在調用方法的時候寫入代碼塊,運行結果如下
2015-06-03 23:32:32.532 WXYBlock[3537:237755] 內聯(lián)block--->王興宇的年齡是26
3.block使用外部變量
//變量的使用?
int myAge = 100;? ?
//獨立block? ?
myBlock = ^ (NSString *name, int age){? ? ? ?
? ? return [NSString stringWithFormat:@"使用變量--->%@的年齡是%d", name, myAge];?
};? ?
NSLog(@"獨立block--->%@", myBlock(@"小宇", 16));? ?
//內聯(lián)block?
[self printWithName:@"王興宇" age:26 block:^(NSString *str, int age){? ? ?
? ? return [NSString stringWithFormat:@"使用變量--->%@的年齡是%d", str, myAge];?
}];
block內部可以直接使用外部定義的變量,運行結果如下
#注意:此處為了方便,直接用myAge代替了原來的age,所以參數16傳進去根本沒有使用。
2015-06-03 23:32:32.533 WXYBlock[3537:237755] 獨立block--->使用變量--->小宇的年齡是1002015-06-03
23:32:32.533 WXYBlock[3537:237755] 內聯(lián)block--->使用變量--->王興宇的年齡是100
一個有趣的現(xiàn)象:
現(xiàn)在你定義了一個獨立block,并且這個block使用了外部的變量。
然后這個變量被改變了,然后你調用了這個block。
注意,是這樣的順序:
定義獨立block并且使用外部變量—→外部變量改變—→調用block
//外部改變變量? ?
myAge = 50;? ?
NSLog(@"獨立block--->變量在外部被改變--->%@", myBlock(@"小宇", 16));? ? ? ?
[self printWithName:@"王興宇" age:26 block:^(NSString *str, int age){
? ? ? return [NSString stringWithFormat:@"變量在外部被改變--->使用變量--->%@的年齡是%d", str, myAge];?
}];
這時候的輸出是
2015-06-03 23:32:32.533 WXYBlock[3537:237755] 獨立block--->變量在外部被改變--->使用變量--->小宇的年齡是100
2015-06-03 23:32:32.533 WXYBlock[3537:237755] 內聯(lián)block--->變量在外部被改變--->使用變量--->王興宇的年齡是50
這是為什么呢?
根據查閱,我總結的原因是這樣的:
block中如果使用了外部變量,他會拷貝一份這個變量,并且這個變量是只讀的。
所以外部變量改變并不影響block內部拷貝的那一份變量。
代碼中的內聯(lián)block是在變量改變后才使用這個變量的,所以并不影響。
如果不想讓block拷貝變量,想讓內部使用的變量和外部使用的變量指向同一地址的話,
需要在變量前面加上__block關鍵字。
像這樣:
__block int myAge = 100;
輸出就變成了:
2015-06-03 23:32:32.533 WXYBlock[3537:237755] 獨立block--->變量在外部被改變--->使用變量--->小宇的年齡是50
2015-06-03 23:32:32.533 WXYBlock[3537:237755] 內聯(lián)block--->變量在外部被改變--->使用變量--->王興宇的年齡是50
另外值得一提的是,加上__block關鍵字之后,外部變量不再是只讀的,在block內部也可以改變它的值。
//改變變量加__block關鍵字? ?
__block int otherAge = 100;?
myBlock = ^ (NSString *name, int age){
? ? ? ? otherAge = 99;
? ? ? ? return [NSString stringWithFormat:@"改變變量加__block--->%@的年齡是%d", name, otherAge];? ?
};? ?
NSLog(@"獨立block--->%@", myBlock(@"小宇", 16));? ? ? ?
[self printWithName:@"王興宇" age:26 block:^(NSString *str, int age){
? ? ? ? otherAge = 98;
? ? ? ? return [NSString stringWithFormat:@"改變變量加__block--->%@的年齡是%d", str, otherAge];? ?
}];
輸出如下:
2015-06-03 23:32:32.534 WXYBlock[3537:237755] 獨立block--->改變變量加__block--->小宇的年齡是99
2015-06-03 23:32:32.534 WXYBlock[3537:237755] 內聯(lián)block--->改變變量加__block--->王興宇的年齡是98
#注意:如果不加__block關鍵字,在block內部改變外部變量的值的話,編譯會報錯!
4.block循環(huán)引用的問題
這部分我的理解可能不太深入,下面只說一下我自己簡單的理解。
首先在self類中聲明一個NSString的屬性
#import@interface ViewController : UIViewController
@property (strong, nonatomic) NSString *myStr;
@end
初始化這個屬性
self.myStr = @"myStr";
使用內聯(lián)block的另一個方法,為了方便,還用之前聲明的block,只是參數用不到了
- (void)printWithblock:(WXYTestBlock)block{
? ? block(@" ", 0);
}
現(xiàn)在,你想要在block中使用self,或者使用self.myStr
如果,self的類中包含block,block中又引用了self
這樣就會造成循環(huán)引用。
解決的方法如下
//使用self和self的屬性
//加__weak避免循環(huán)引用
__weak ViewController *weakSelf = self;
//獨立
myBlock = ^ (NSString *name, int age){
NSLog(@"獨立Block使用self--->%@", weakSelf);
NSLog(@"獨立Block使用self的屬性--->%@", weakSelf.myStr);
return @" ";
};
myBlock(@"", 0);
//內聯(lián)
[self printWithblock:^(NSString *name, int age){
NSLog(@"內聯(lián)Block使用self--->%@", weakSelf);
NSLog(@"內聯(lián)Block使用self的屬性--->%@", weakSelf.myStr);
return @" ";
}];
將self轉化成為一個用__weak修飾的weakSelf,就可以避免循環(huán)引用。
#注意:只有self中包含block的引用,并且block內使用了self才會循環(huán)引用。不過為了保險起見,所有block內用到self的還是加上__weak為好。
以上是我個人對block至今為止全部的理解,希望對初學者有一定的幫助。
有不足和錯誤之處,歡迎指正。