1. block的實(shí)質(zhì)是什么佛吓?一共有幾種block?都是什么情況下生成的李茫?
block的實(shí)質(zhì)是什么十电?
- block本質(zhì)上也是一個(gè)OC對象知押,它內(nèi)部也有個(gè)isa指針
- block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象
- block是封裝函數(shù)及其上下文的OC對象
查看block源碼:
- 打開終端,在main.m所在目錄下鍵入clang -rewrite-objc main.m即可在當(dāng)前目錄下生成一個(gè)main.cpp文件;
- 當(dāng)引用了OC中的Foundation或者UIKit框架時(shí)鹃骂,通過 clang -rewrite-objc 指定文件名 命令將指定文件轉(zhuǎn)換成C++代碼會(huì)報(bào)錯(cuò);
- 可通過 clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk 指定文件名
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台盯?
- _NSConcreteGlobalBlock 全局靜態(tài)
- _NSConcreteStackBlock 保存在棧中,出函數(shù)作用域就銷毀
- _NSConcreteMallocBlock 保存在堆中畏线,retainCount == 0銷毀而ARC和MRC中静盅,還略有不同;
都是什么情況下生成的?
1寝殴、在全局區(qū)域聲明定義一個(gè)block
2蒿叠、block表達(dá)式中沒有使用捕獲的自動(dòng)變量時(shí)
以上情況生成的block都是NSConcreteGlobalBlock類型,只生成一個(gè)結(jié)構(gòu)體實(shí)例蚣常;除此情況下市咽,生成的都是NSConcreteStackBlock類型的block,且都是存儲(chǔ)在棧上史隆。
參考2:又見block系列(值得再看一遍)
參考3:《Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理》電子書參考第二章
2. 為什么在默認(rèn)情況下無法修改被block捕獲的變量魂务? __block都做了什么?
Block只捕獲Block中會(huì)用到的變量泌射。由于只捕獲了自動(dòng)變量(自動(dòng)變量是以值傳遞方式傳遞到Block的構(gòu)造函數(shù)里面)的值,并非內(nèi)存地址鬓照,所以Block內(nèi)部不能改變自動(dòng)變量的值熔酷。Block捕獲的外部變量可以改變值的是靜態(tài)變量,靜態(tài)全局變量豺裆,全局變量拒秘。
參考:深入研究Block捕獲外部變量和__block實(shí)現(xiàn)原理
如何在block中修改外部自動(dòng)變量的值号显,主要有以下2種方法:
- a、修改C語言中的靜態(tài)變量躺酒、靜態(tài)全局變量和全局變量
- b押蚤、使用__block修飾需要修改的自動(dòng)變量
3. 模擬一下循環(huán)引用的一個(gè)情況?block實(shí)現(xiàn)界面反向傳值如何實(shí)現(xiàn)羹应?
循環(huán)引用:
#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界面反向傳值實(shí)例:
1揽碘、在第二個(gè)視圖控制器的.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];
}
2、在第一個(gè)視圖中獲得第二個(gè)視圖控制器园匹,并且用第二個(gè)視圖控制器來調(diào)用定義的屬性:
// 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];
}
4. NSTImer的循環(huán)引用問題
使用NSTimer可能會(huì)碰到循環(huán)引用的問題雳刺。特別是當(dāng)類具有NSTimer類型的成員變量,并且需要反復(fù)執(zhí)行計(jì)時(shí)任務(wù)時(shí)裸违。例如
_timer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(startCounting) userInfo:nil
repeats:YES];
類有一個(gè)成員變量_timer,給_timer設(shè)置的target為這個(gè)類本身。這樣類保留_timer镣奋,_timer又保留了這個(gè)類胁勺,就會(huì)出現(xiàn)循環(huán)引用的問題,最后導(dǎo)致類無法正確釋放怔昨。
解決這個(gè)問題的方式也很簡單料饥,當(dāng)類的使用者能夠確定不需要使用這個(gè)計(jì)時(shí)器時(shí),就調(diào)用
[_timer invalidate];
_timer = nil;
這樣就打破了保留環(huán)朱监,類也可以正確釋放岸啡。但是,這種依賴于開發(fā)者手動(dòng)調(diào)用方法赫编,才能讓內(nèi)存正確釋放的方式不是一個(gè)非常好的處理方式巡蘸。所以需要另外一種解決方案。
如下所示:
@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
定義一個(gè)NSTimer的類別擂送,在類別中定義一個(gè)類方法悦荒。類方法有一個(gè)類型為塊的參數(shù)(定義的塊位于棧上,為了防止塊被釋放嘹吨,需要調(diào)用copy方法搬味,將塊移到堆上)。
使用這個(gè)類別的方式如下:
__weak ViewController *weakSelf = self;
_timer = [NSTimer jq_scheduledTimerWithTimeInterval:5.0
block:^{
__strong ViewController *strongSelf = weakSelf;
[strongSelf startCounting];
}
repeats:YES];
使用這種方案就可以防止NSTimer對類的保留蟀拷,從而打破了循環(huán)引用的產(chǎn)生碰纬。__strong ViewController *strongSelf = weakSelf主要是為了防止執(zhí)行塊的代碼時(shí),類被釋放了问芬。在類的dealloc方法中悦析,記得調(diào)用[_timer invalidate]。
相關(guān)閱讀:
1此衅、iOS 面試題 --- 基礎(chǔ)部分
2强戴、iOS 面試題 --- 中級(jí)篇 Runtime
3亭螟、iOS 面試題 --- 中級(jí)篇 Runloop
4、iOS 面試題 --- 高級(jí)篇