文档章节

回炉篇之(五)-- OC 中的 Block

高考这件小事
 高考这件小事
发布于 2015/05/22 13:27
字数 1442
阅读 21
收藏 0

原文发表在 http://pupboss.com/objective-c-block/

回炉篇已经是第五篇了,断断续续的写,前面几篇附上一个传送门吧


这篇可能会比较长,先列个提纲

  • 一个最简单的 Block
  • 有参数无返回值的 Block
  • 有参数有返回值的 Block
  • Block 中的 typedef
  • Block 到底(比函数)优越在哪里
  • Block 的进阶使用

Objective-C 的 Block 非常神奇,相当于一个类,里面保存的是一段代码(代码块),别的语言可能还没有这个特性,同时,Apple 官方也推荐开发者使用 Block 来写程序。

一个最简单的 Block

Block 跟函数挺像的,不过用法比函数要高大上。举个栗子:

// 函数是这样写
void blockTest() {
    NSLog(@"Hello, World!");
}

// 这样调用
blockTest();

// block 这样写
void (^blockTest)() = ^{
    NSLog(@"Hello, World!");
};

// 这样调用
blockTest();

两个调用是完全一样的,不过 Block 的声明有点难理解,来分析下

void 说明函数没有返回值,跟函数的写法完全一样的,

(^blockTest) 拆成三部分,blockTest 是 Block 的名字,前面加一个 ^ 说明这是个 Block 类型,然后再用括号括住,这是固定写法,

() 说明无参数,

^{} 这个就是 Block 内容的固定写法了,当然前提是==没有参数==,另一种情况稍后补充慢慢来~

有参数无返回值的 Block

void (^blockTest)(int, NSString *) = ^(int count, NSString *word){
    
    for (int i = 0; i<count; i++) {
        NSLog(@"%@", word);
    }
};

blockTest(5, @"Hello, World!");

大括号内的东西大概都明白,不再细说,上面提到,() 代表无参数,那么这次里面有一个 int, NSString *,说明有两个类型的参数,后面原本是 ^{},现在中间多了个 (),道理同上。

有参数有返回值的 Block

int (^sum)(int, int) = ^(int x, int y){
    
    return x + y;
};

NSLog(@"%d", sum(13, 14));

这个其实我不想多说了,以大家聪明的程度肯定自己能看懂。

Block 中的 typedef

大家都知道 typedef 可以这么用

typedef int BlockInt;

相同的,可以推理到 Block 中

typedef int (^MyBlock)(int);

==注意写法,跟普通的不太一样==

之后想用的时候,可以直接把 MyBlock 当成一个类型,举个栗子

MyBlock square = ^(int x){
    return x * x;
};

square(6);

Block 到底(比函数)优越在哪里

void getSomeInfo(NSString *info) {
    
    NSLog(@"%@", info);
}

用函数写的话,可以这么玩,传入一个已知类型的变量,由这个函数来决定如何处置变量。

我们写程序讲究低耦合,这个函数关心的东西越少越好,这么做管的太宽了,但是我们有 Block,来看一个神奇的效果:

void getSomeInfo(void(^blockFunc)()) {
    
    NSLog(@"Before Block");
    blockFunc();
    NSLog(@"After Block");
}

main 函数中

getSomeInfo(^{
    
    NSLog(@"%@", @"You can do anything here");
});

结果是

2015-05-21 22:52:23.498 Block-Test[5427:277676] Before Block
2015-05-21 22:52:23.499 Block-Test[5427:277676] You can do anything here
2015-05-21 22:52:23.499 Block-Test[5427:277676] After Block

当然这么搞不太严谨的,如果传进去一个空,程序会报错(坏访问),解决方法也很简单,判断一下是否为空即可,我就不写了 =. =

Block 的进阶使用

  1. 修改外部变量的时候,外部变量需要加一个 __block 来修饰

    __block int a = 50;

  2. 为什么加上 __block 就能修改了

先贴 main.m 的代码

//
//  main.m
//  Block-Test
//
//  Created by JieLee on 15/5/21.
//  Copyright (c) 2015年 PUPBOSS. All rights reserved.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        __block int a = 10;
        
        void (^blockTest)() = ^{
            
            a = 50;
            NSLog(@"%d", a);
        };

        blockTest();
        
    }
    return 0;
}

在 Terminal 中,输入 clang -rewrite-objc main.m,居然转换了 10 万多行 C++ 代码,拉到最下面分析

有一段是这样的

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};

        void (*blockTest)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344);

        ((void (*)(__block_impl *))((__block_impl *)blockTest)->FuncPtr)((__block_impl *)blockTest);

    }
    return 0;
}

来分析下,一般括号内的东西是强转,那就好办了,重点看这句

void (*blockTest)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344);

删掉各种括号,就变成了

void (*blockTest)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &a, 570425344);

__main_block_impl_0 应该是一个类,或者结构体,我们往上找,还真有一个

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

C++ 的结构体比较有意思,他可以调用自己的 __main_block_impl_0 函数,然后把需要的东西传进去,最后 Desc = desc; 返回一个结构体。

再转回来 &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &a, 570425344); 是什么意思呢,返回一个结构体,然后取地址,所以 block 的本质,就是一个结构体!

至于为什么能改变 a 的值,我贴段代码,大家自己理解吧,我是有点蒙。反正就是各种地址各种传进去,然后就把值给改了。

__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref


            (a->__forwarding->a) = 50;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_f__kvyhp79j5wbcb2d7t24v3v_c0000gn_T_main_f979f0_mi_2, (a->__forwarding->a));
        }

文章最后有完整的代码,想研究的可以去看看~

附完整代码

Gist

© 著作权归作者所有

高考这件小事
粉丝 1
博文 1
码字总数 1442
作品 1
鹤壁
程序员
私信 提问
Swift3.0 函数闭包与 Block

最近新接手了一个Swift项目,花点时间做点小结,以做记录 我的博客链接 Swift中定义一个基本函数 这里下划线 的作用用来忽略外部參数名,具体可以参考这篇文章 带参函数定义 举栗子,网络数据...

PetitBread
2017/06/08
0
0
Swift4.0基础知识回顾

简单介绍一下Swift Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序,Swift语言让应用开发更简单、更快、更稳定,确保最终应用有着更好的质量更多了解Swift可以去《The...

博行天下
2017/12/06
0
0
iOS 中的链式编程、函数式编程入门

对一个程序猿来说,从一开始接触编程后会逐渐经历好几个不同的编程思想。包括过程式编程、面向对象编程、响应式编程、函数式编程、链式编程等等。 的特点是随着程序的编写逐步进行,写到哪儿...

BennyLoo
2017/11/08
0
0
iOS下JS与原生OC互相调用(精品)

iOS开发免不了要与UIWebView打交道,然后就要涉及到JS与原生OC交互,今天总结一下JS与原生OC交互的两种方式。 JS调用原生OC篇 方式一 第一种方式是用JS发起一个假的URL请求,然后利用UIWebVi...

小王爷还小
2016/11/17
4
0
swift的数据类型到底是什么尿性 (说点别人没讲明白的)

文初: 如果你对swift的些许了解只局限在 swift中的类型使用struct取代class 多了Optional可选类型 这些最基础的认知,而对其底层设计的原因和原理了解甚少,那这篇文章会给你新的视角,让你...

CoderDancer
2017/08/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
17
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
9
0
太全了|万字详解Docker架构原理、功能及使用

一、简介 1、了解Docker的前生LXC LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpa...

Java技术剑
今天
22
0
Wifiphisher —— 非常非常非常流氓的 WIFI 网络钓鱼框架

编者注:这是一个非常流氓的 WIFI 网络钓鱼工具,甚至可能是非法的工具(取决于你的使用场景)。在没有事先获得许可的情况下使用 Wifiphisher 攻击基础网络设施将被视为非法活动。使用时请遵...

红薯
今天
92
1
MongoDB 4 on CentOS 7安装指南

本教程为CentOS x86_64 7.x操作系统下,MongoDB Community x86_64 4.2(GA)安装指南。 安装方式一:yum repo在线安装 [此方式较为简单,官方推荐] Step1:新建MongDB社区版Yum镜像源。 # vim ...

王焱君
今天
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部