【瞎搞iOS开发09】聊一聊Block和[Block copy]


  • 本文扯些啥
  • 一种取消GCD延时任务的解决方案
  • 非常简单的介绍3种Block类型
  • ARC模式打印引用计数retianCount
  • __NSGlobalBlock__、__NSStackBlock__、__NSMallocBlock__测试
  • ARC模式声明block属性不要用weak
  • ARC模式声明block属性用copy、strong都可以

一种取消GCD延时任务的解决方案

在项目中经常用到延时操作,但是dispatch_after有个缺点,不能像perform~方法那样可以随时取消延时,有时会导致控制器被Block强引用而延时释放,可能导致崩溃,安全性不高。所以想实现一个可以取消的GCD延时任务的功能,但是折腾了很久没跳出这个坑,最后在Github上找到了一种解决方案。【Dispatch-Cancel】,代码虽少,但是效果极好,下面是源码。

typedef void(^SMDelayedBlockHandle)(BOOL cancel);

static SMDelayedBlockHandle perform_block_after_delay(CGFloat seconds, dispatch_block_t block) {
    if (nil == block) {
        return nil;
    }

    __block dispatch_block_t blockToExecute = [block copy];
    __block SMDelayedBlockHandle delayHandleCopy = nil;
    
    SMDelayedBlockHandle delayHandle = ^(BOOL cancel){      
        if (NO == cancel && nil != blockToExecute) {
            dispatch_async(dispatch_get_main_queue(), blockToExecute);
        }
    
#if !__has_feature(objc_arc)
        [blockToExecute release];
        [delayHandleCopy release];
#endif

        blockToExecute = nil;
        delayHandleCopy = nil;
    };
    delayHandleCopy = [delayHandle copy];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        if (nil != delayHandleCopy) {
            delayHandleCopy(NO);
        }
    });

    return delayHandleCopy;
};

static void cancel_delayed_block(SMDelayedBlockHandle delayedHandle) {
    if (nil == delayedHandle) {
        return;
    }
    delayedHandle(YES);
}

这是我们常规写法

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), dispatch_get_main_queue(), {
    /// do something ,并且强引用外部对象,执行完才release
});

对比常规写法,源码的采用__block修饰的blockToExecute代替传进来的block,并且加了一个__block修饰的桥梁(中介) delayHandleCopy实现解耦,dispatch_after不会直接强引用外部对象,大致的引用关系如下,这样释放2个__block修饰的桥梁block,就可以解除了dispatch_after对外部对象的强引用。对NSTimer、KVO啥的都可以借助这种思想,利用桥梁对象实现解耦。

dispatch_after --> (__block) delayHandleCopy -> delayHandle --> (__block) blockToExecute -> block -> 外部对象

之前看源码的时候发现有2处用到了[block copy],比如下面的代码,以前没这么用过,就看了一些博客学习,然后自己捯饬。

__block dispatch_block_t blockToExecute = [block copy];

源码兼容了MRC,所以我把MRC相关的代码去掉,并且把2个[block copy]也去掉,一捯饬,没毛病! 但还是搞不懂为啥MRC要加[block copy],而ARC可以不用copy,为啥声明Block属性要用copy?,所以继续瞎捯饬了两天,在这做个粗略总结。


三种Block类

这里只做简单介绍,不会介绍底层的那些结构体源码,看后面的代码也能大概了解block,如果想深入了解,简书也有很多相关的文章。
首先Block分3种Class,分别是__NSGlobalBlock____NSStackBlock____NSMallocBlock__,主要在于内存区域的不同,其次是__NSGlobalBlock__不会引用任务外部变量,__NSStackBlock__会引用外部变量,__NSMallocBlock__[__NSStackBlock__ copy]而来,所以也会引用外部变量,这里说的引用外部变量指的是在block里面直接引用Block外的变量对象,而不是引用传入block里面的参数对象。至于三种Block在内存中的储存位置,我找了一张图,来自【iOS Block源码分析系列】。如果对内存分区好奇,看看这篇文章【iOS内存分配和分区】

3种Block的储存位置


ARC模式打印引用计数retianCount

先介绍一种在ARC模式下打印引用计数的方法,但是使用CFGetRetainCount获取引用计数会比实际引用计数大1,所以下面做了-1操作(对一个对象只会+1一次,不会叠加),只是不适合打印字符串对象的引用计数(数值特别大),此文不会扯MRC基础原理,当然有疑问也可以讨论。

static inline void JKLogRetainCount(NSString * des ,id obj) {
    if (nil != obj) {
        /// 实际的RetainCount 比 CFGetRetainCount 小 1
        NSLog(@"%@  RetainCount = %zd", des,CFGetRetainCount((__bridge CFTypeRef)obj) - 1);
    } else {
        NSLog(@"%@  RetainCount = 0, obj == nil",des);
    }
}

NSGlobalBlock测试

    UIView * testView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
    testView.backgroundColor = [UIColor orangeColor];
    JKLogRetainCount(@"alloc testView",testView);
    
    [self.view addSubview:testView];
    JKLogRetainCount(@"add testView",testView);
    
     dispatch_block_t globalBlock = ^(){};
     NSLog(@"1:%@",globalBlock);
     NSLog(@"2:%@",^(int a, int b){ a = a + b;});
    
     void (^globalBlock_Temp) (UIView * , int ) = ^(UIView * a, int b) {
         a.backgroundColor = [UIColor redColor];
         JKLogRetainCount(@"作为GlobalBlock内部的参数 testView",a);
     };
    
    JKLogRetainCount(@"GlobalBlock外部的testView",testView);
    globalBlock_Temp(testView, 3);
    JKLogRetainCount(@"已执行完GlobalBlock,外部testView",testView);
    NSLog(@"3:%@",globalBlock_Temp);
    
    /// 作为方法的参数传入
    [self globalBlockTest:^{
        NSLog(@"没有强引用外部变量");
    }];
    
    
    - (void)globalBlockTest:(void(^)(void))globalBlock {
    NSLog(@"globalBlock Func Log:%@",globalBlock);
}

打印结果:

     [控制台打印] alloc testView  RetainCount = 1
     [控制台打印] add testView  RetainCount = 2
     [控制台打印] 1:<__NSGlobalBlock__: 0x10fa44100>
     [控制台打印] 2:<__NSGlobalBlock__: 0x10fa44140>
     [控制台打印] GlobalBlock外部的testView  RetainCount = 2
     [控制台打印] 作为GlobalBlock内部的参数 testView  RetainCount = 3
     [控制台打印] 已执行完GlobalBlock,外部testView  RetainCount = 2
     [控制台打印] 3:<__NSGlobalBlock__: 0x10fa44180>
     [控制台打印] 作为GlobalBlock内部的参数 testView  RetainCount = 3
     [控制台打印] globalBlock Func Log:<__NSGlobalBlock__: 0x10fa441c0>

上面4种情况中的Block都没有引用外部变量,不管有没有赋值操作,都是__NSGlobalBlock__类型。另外作为参数传入Block的外部对象,只有在执行GlobalBlock时,GlobalBlock会强引用参数变量(RetainCount+1),GlobalBlock执行结束就会解除强引用(RetainCount-1)。
在Block对对象参数的引用上,__NSGlobalBlock_、__NSStackBlock__、__NSMallocBlock__都是一样的效果,即在执行Block的过程中,Block会强引用参数对象一次(RetainCount+1),执行完就会解除强引用(RetainCount-1)。如果要在Block里面调用Block外部的对象,传参是最安全的,但使用起来比较麻烦,代码不是很好理解。


NSStackBlock测试

    testView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 50, 50)];
    JKLogRetainCount(@"alloc testView",testView);
    
    [self.view addSubview:testView];
    JKLogRetainCount(@"add testView",testView);

    NSLog(@"1:%@",^(){
        testView.backgroundColor = [UIColor darkGrayColor];
        JKLogRetainCount(@"stackBlock内部强引用的 testView",testView);
    });
    JKLogRetainCount(@"外部的TestView已被stackBlock引用,但是未调用stackBlock", testView);

    __weak void (^weakStackBlock)(UIView *) = ^(UIView * view){
        testView.backgroundColor = [UIColor darkGrayColor];
        view.backgroundColor = [UIColor darkGrayColor];
        JKLogRetainCount(@"stackBlock内部强引用的 testView",testView);
    };
    NSLog(@"2:%@",weakStackBlock);
    weakStackBlock(testView);
    JKLogRetainCount(@"weakStackBlock已执行完,外部的testView",testView);
    NSLog(@"3:%@",weakStackBlock);
    [self stackBlockTest:weakStackBlock];

    void(^tempStackBlock)(UIView *) = weakStackBlock;
    NSLog(@"tempStackBlock = weakStackBlock   2: %@",tempStackBlock);


- (void)stackBlockTest:(void(^)(UIView *))stackBlock {
     NSLog(@"stackBlock Func Log:%@",stackBlock);
    
    /// tempStackBlock会是__NSMallocBlock__,应该执行了copy
    void(^tempMallocBlock)(UIView *) = stackBlock;
    NSLog(@"tempMallocBlock = stackBlock   1: %@",tempMallocBlock);
}
    /**<  
     [控制台打印] alloc testView  RetainCount = 1
     [控制台打印] add testView  RetainCount = 2
     [控制台打印] 1:<__NSStackBlock__: 0x7fff501be818>
     [控制台打印] 外部的TestView已被stackBlock引用,但是未调用stackBlock  RetainCount = 3
     [控制台打印] 2:<__NSStackBlock__: 0x7fff501be7e8>
     [控制台打印] stackBlock内部强引用的 testView  RetainCount = 5
     [控制台打印] weakStackBlock已执行完,外部的testView  RetainCount = 4
     [控制台打印] 3:<__NSStackBlock__: 0x7fff501be7e8>
     [控制台打印] stackBlock Func Log:<__NSStackBlock__: 0x7fff501be7e8>
     
     ** 重点 **
     [控制台打印] tempMallocBlock = stackBlock   1: <__NSMallocBlock__: 0x60000005b960>
     [控制台打印] tempStackBlock = weakStackBlock   2: <__NSStackBlock__: 0x7fff501be7e8>
     */

__NSStackBlock__的出栈入栈都由系统管理,在定义block时外部变量就会被stackBlock强引用一次,直到stackBlock销毁才会解除强引用。在上面的测试中,testView被2个stackBlock引用(一个未命名,一个是weakStackBlock),所以引用计数被+1了2次。另外testView作为参数传入atackBlock,在执行stackBlock过程中也被retain了一次,不过执行完又release了一次,这点跟GlobalBlock是一样的。值得注意的有3点:

  1. 将一个stackBlock赋值给一个__weak修饰的weakStackBlock,weakStackBlock还是__NSStackBlock__类型,后面的测试中是将普通的stackBlock赋值给一个__strong修饰或者普通的strongBlock,strongBlock会是__NSMallocBlock__类型,原因ARC环境下在block赋值时会自动调用copy。
  2. 将weakStackBlock赋值给一个普通的block,block还是__NSStackBlock__,并且block == weakStackBlock,ARC和MRC环境测试结果一样。
  3. 调用方法时将weakStackBlock作为参数传入方法,在方法内部将weakStackBlock赋值给一个普通的block,将会是__NSMallocBlock__类型,而且block != weakStackBlock,或许__weak作用域/可识别域是有限的,这方面待研究。

NSMallocBlock

    void (^mallocBlock_temp) (UIView *) = [stackBlock copy];
    NSLog(@"1:%@",mallocBlock_temp);

    testView = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 50, 50)];
    testView.backgroundColor = [UIColor orangeColor];
    JKLogRetainCount(@"alloc testView",testView);
    
    [self.view addSubview:testView];
    JKLogRetainCount(@"add testView",testView);
    
    ** 如果把这行放到alloc View前面去,结果会怎么样? ** 
    //void (^mallocBlock_temp) (UIView *) = [stackBlock copy];
    //NSLog(@"1:%@",mallocBlock_temp);
    
    void (^mallocBlock) (UIView *) = ^(UIView * view) {
        testView.backgroundColor = [UIColor redColor];
        view.backgroundColor = [UIColor blueColor];
        JKLogRetainCount(@"mallocBlock内部的testView", testView);
    };
    
    NSLog(@"2:%@",mallocBlock);
    mallocBlock(testView);
    JKLogRetainCount(@"已调用mallocBlock,testView被强引用,也作为参数传入mallocBlock", testView);
    
    [self mallocBlockTest:mallocBlock];
    
    void (^mallocBlock_Copy)(UIView *) = [mallocBlock copy];
    NSLog(@"mallocBlock_Copy %@",mallocBlock_Copy);
    JKLogRetainCount(@"copy mallocBlock之后", testView);
    
    __strong void (^mallocBlock_Copy_Copy)(UIView *) = [mallocBlock_Copy copy];
    NSLog(@"mallocBlock_Copy_Copy %@",mallocBlock_Copy_Copy);
    JKLogRetainCount(@"copy mallocBlock_Copy_Copy之后", testView);
     [控制台打印] alloc testView  RetainCount = 1
     [控制台打印] add testView  RetainCount = 2
     [控制台打印] 1:<__NSMallocBlock__: 0x6000002409f0>
     [控制台打印] 2:<__NSMallocBlock__: 0x600000240b70>
     [控制台打印] mallocBlock内部的testView  RetainCount = 5
     [控制台打印] 已调用mallocBlock,testView被强引用,也作为参数传入mallocBlock  RetainCount = 4
     [控制台打印] mallocBlock Func Log:<__NSMallocBlock__: 0x600000240b70>
     [控制台打印] mallocBlock内部的testView  RetainCount = 4
     [控制台打印] mallocBlock_Copy <__NSMallocBlock__: 0x600000240b70>
     [控制台打印] copy mallocBlock之后  RetainCount = 4
     [控制台打印] mallocBlock_Copy_Copy <__NSMallocBlock__: 0x600000240b70>
     [控制台打印] copy mallocBlock_Copy_Copy之后  RetainCount = 4

在ARC模式下,将stackBlock赋值给一个不被__weak修饰的mallocBlock,系统会自动执行[stackBlockw copy],所以__NSMallocBlock__是我们经常见的block类型。并且我在MRC环境下测试,[stackBlock retain]还是__NSStackBlock__类型,只有[stackBlock copy]会产生一个__NSMallocBlock__类型的block,所以系统是自动执行了copy。所以前面我删了部分源码在ARC模式下也能运行,是系统帮我们调用了copy,而copy globalBlock和mallocBlock还是原类型,像浅拷贝,所以我们再调用copy也无碍。

外部变量obj开始会被stackBlock强引用一次,在[stackBlock copy]过程又会被强引用一次,所以外部变量会被强引用2次,有点像深拷贝的效果。另外3种类型的block调用copy有不同的结果:

  • stackBlock != [stackBlcok copy]
  • mallocBlock == [mallocBlock copy]
  • globalBlock == [globalBlock copy]

[stackBlcok copy]像深拷贝,而[mallocBlock copy]和[globalBlock copy]则像浅拷贝。PS:只能说像,并期待大神解答。


ARC模式声明block属性不要用weak

之所以不能用weak,是因为存在很大的安全隐患。比如在ViewDidLoad中将一个stackBlock赋值给self.weakBlock,在viewDidAppear中调用self.block(),会出现崩溃,原因是self.weakBlock在viewDidLoad方法结束后会被系统释放掉,self.weakBlock == nil。

@property (nonatomic, weak) void(^weakBlock)();

- (void)viewDidLoad {
    [super viewDidLoad];
      
    testView = [[UIView alloc] initWithFrame:CGRectMake(100, 400, 50, 50)];
    [self.view addSubview:testView];
    
    self.weakBlock = ^(){
        testView.backgroundColor = [UIColor redColor];
        JKLogRetainCount(@"self.weakBlock 内部testView", testView);
    };
}


- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    /// 会崩溃
    self.weakBlock();
}


ARC环境下声明block属性,copy和strong都可以

@property (nonatomic, copy) void(^copyBlock)();
@property (nonatomic, strong) void(^strongBlock)();
    testView = [[UIView alloc] initWithFrame:CGRectMake(100, 400, 50, 50)];
    testView.backgroundColor = [UIColor orangeColor];
    JKLogRetainCount(@"alloc testView",testView);
    
    [self.view addSubview:testView];
    JKLogRetainCount(@"add testView",testView);

    self.copyBlock = ^(){
        testView.backgroundColor = [UIColor blueColor];
        JKLogRetainCount(@"self.copyBlock 内部testView", testView);
    };
    NSLog(@"2:%@",self.copyBlock);
    self.copyBlock();
    JKLogRetainCount(@"self.copyBlock 外部testView", testView);
    
    
     [控制台打印] alloc testView  RetainCount = 1
     [控制台打印] add testView  RetainCount = 2
     [控制台打印] 2:<__NSMallocBlock__: 0x60800044d080>
     [控制台打印] self.copyBlock 内部testView  RetainCount = 4
     [控制台打印] self.copyBlock 外部testView  RetainCount = 4
    
    testView = [[UIView alloc] initWithFrame:CGRectMake(100, 400, 50, 50)];
    testView.backgroundColor = [UIColor orangeColor];
    JKLogRetainCount(@"alloc testView",testView);
    
    [self.view addSubview:testView];
    JKLogRetainCount(@"add testView",testView);
    
    self.strongBlock = ^(){
        testView.backgroundColor = [UIColor blueColor];
        JKLogRetainCount(@"self.strongBlock 内部 testView", testView);
    };
    NSLog(@"3:%@",self.strongBlock);
    self.strongBlock();
    JKLogRetainCount(@"self.strongBlock 外部 testView", testView);


     [控制台打印] alloc testView  RetainCount = 1
     [控制台打印] add testView  RetainCount = 2
     [控制台打印] 3:<__NSMallocBlock__: 0x60800044d050>
     [控制台打印] self.strongBlock 内部 testView  RetainCount = 4
     [控制台打印] self.strongBlock 外部 testView  RetainCount = 4
- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];

        self.copyBlock();
        self.strongBlock();
    
        NSLog(@"self.copyBlock   :%@",self.copyBlock);
        NSLog(@"self.strongBlock :%@",self.strongBlock);

    
    /**<  
     [控制台打印] self.copyBlock 内部testView  RetainCount = 3
     [控制台打印] self.strongBlock 内部 testView  RetainCount = 3
     [控制台打印] self.copyBlock   :<__NSMallocBlock__: 0x60800044d080>
     [控制台打印] self.strongBlock :<__NSMallocBlock__: 0x60800044d050>
     */
}

从打印结果来看,外部变量testView的引用计数在Self.copyBlock和self.strongBlock强引用后都+2,也就是testView被强引用了2次效果一致。stackBlock赋值给self.copyBlock时,系统会执行[stackBlock copy],另外在setter方法里面也会执行copy。赋值给self.strongBlock的结果和赋值给self.copyBlock是一样的,上面提到过,在MRC模式下,[stackBlock retain]不会产生__NSMallocBlock__类型的block对象,所以我想苹果是对Block的赋值做了特殊处理。总之在ARC模式下,声明copy和strong类型的block属性效果是一样的,不会影响使用。

Block的原始类型 newBlock = [Block copy]
newBlock的类型
__NSGlobalBlock__ __NSGlobalBlock__
__NSStackBlock__ __NSMallocBlock__
__NSMallocBlock__ __NSMallocBlock__
  • stackBlock != [stackBlcok copy]
  • mallocBlock == [mallocBlock copy]
  • globalBlock == [globalBlock copy]

另外发现一个现象,声明Block、NSString、NSDictionary、NSArray属性(self.property)都适合用copy,它们的共同点是 self.property == [self.property copy],而且[self.property copy]这个过程都像or是浅拷贝。

所以在此提出一个猜测:"除blcok对象以外,可以用 obj == [obj copy]来判断对象obj是否可变",欢迎讨论。

猜完了再看看这篇文章

下一篇文章设计copy和mutableCopy对(NSString、NSDictionary、NSArray对象)的影响,并涉及深拷贝、浅拷贝。


参考门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,966评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,170评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,909评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,959评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,851评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,583评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,956评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,590评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,878评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,892评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,719评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,501评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,957评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,124评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,440评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,003评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,211评论 2 339

推荐阅读更多精彩内容

  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,751评论 0 23
  • Block 梳理与疑问 时隔一年,再次读 《Objective-C 高级编程》,看到 block 一章,这一次从头...
    DeerRun阅读 619评论 0 2
  • iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,B...
    smile刺客阅读 2,327评论 2 26
  • 《Objective-C高级编程》这本书就讲了三个东西:自动引用计数、block、GCD,偏向于从原理上对这些内容...
    WeiHing阅读 9,785评论 10 69
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,108评论 29 470