依照Metronic的Bootstrap开发框架经验总计(17)– 使用 summernote插件达成HTML文档的编纂和图纸插入操作

1. Block

在众多场所,大家要求在线编辑HTML内容,然后在页面上或者其余终端上(如小程序、APP应用等)展现,编辑HTML内容的插件有很多,本篇介绍基于Bootstrap的 summernote插件完毕HTML文档的编排和图片插入操作,那么些控件的使用极度便宜,并且用户群也很大。

1.1 什么是Block

  以前都是对block的粗略实用,那里再度驾驭下。

  代码块Block是苹果在iOS4发端引入的对C语言的壮大,达成匿名函数的特性,Block是一种奇特的数据类型,其可以正常定义变量、作为参数、作为再次回到值,特殊的,block还足以保留一段代码,在需求的时候调用,近日Block广泛的使用iOS开发中,常用来GCD、动画、排序及各项回调。

  注:Block的宣示与赋值只是保存了一段代码段,必须调用才能实施内部的代码。  

Summernote
是一个大概利落,所见即所得(WYSIWYG)的编辑器,Summernote是一个轻量级、灵活基于Bootstrap和jQuery的HTML文本编辑器,拥有无敌的API配置效应,多国语言支持协理Bootstrap2.x和3.0,帮助视频和图纸上传以及代码的高亮显示,多种
后台语言版本示例以及双焦点扶助,在浏览器包容方面,协理IE9+以及现代的浏览器Chrome,Firefox,Safari等,在活动端应该有不利的突显,照旧很不利的,值得使用。

1.2 Block不难的利用

Block的声明:

Block变量的声明格式为: 返回值类型(^Block名字)(参数列表);

// 声明一个无返回值,参数为两个字符串对象,叫做aBlock的Block
void(^aBlock)(NSString *x, NSString *y);

// 形参变量名称可以省略,只留有变量类型即可
void(^aBlock)(NSString *, NSString *);

 Block的赋值:

Block变量的赋值格式为: Block变量 = ^(参数列表){函数体};

aBlock = ^(NSString *x, NSString *y){
    NSLog(@"%@ love %@", x, y);
};

Block注脚并赋值:

int(^myBlock)(int) = ^(int num){
    return num * 7;
};

// 如果没有参数列表,在赋值时参数列表可以省略
void(^aVoidBlock)() = ^{
    NSLog(@"I am a aVoidBlock");
};

Block 变量的调用;

// 调用后控制台输出"Li Lei love Han Meimei"
aBlock(@"Li Lei",@"Han Meimei");

// 调用后控制台输出"result = 63"
NSLog(@"result = %d", myBlock(9));

// 调用后控制台输出"I am a aVoidBlock"
aVoidBlock();

该插件是开源的,官网地址:http://summernote.org/,GitHub地址:https://github.com/summernote/summernote/、安装调整都很有利。

2. Block 数据结构

1、Summernote的简约利用

运用格局:

1)、加载JS和CSS

<!-- include libraries(jQuery, bootstrap) -->
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script> 
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js"></script> 

<!-- include summernote css/js-->
<link href="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.7/summernote.css" rel="stylesheet">
<script src="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.7/summernote.js"></script>

据悉MVC的Asp.net应用程序中,大家一般在BundleConfig.cs里面开首化大家的引入JS文件,如下所示。

            //添加对bootstrap-summernote的支持
            css_metronic.Include("~/Content/metronic/assets/global/plugins/bootstrap-summernote/summernote.css");
            js_metronic.Include("~/Content/metronic/assets/global/plugins/bootstrap-summernote/summernote.min.js");
            js_metronic.Include("~/Content/metronic/assets/global/plugins/bootstrap-summernote/lang/summernote-zh-CN.min.js");

            bundles.Add(css_metronic);
            bundles.Add(js_metronic);

 

2)、编写HTML内容

在HTML页面代码增加上边的标志。

<div id="summernote">Hello Summernote</div>

 

3)、开首化调用

简易的初步化代码如下所示。

$(document).ready(function() {
  $('#summernote').summernote();
});

如果扩张控件高度的定义,则如下所示。

$('#summernote').summernote({
  height: 300,   
  focus: true    
});

而详尽的开头化界面,一般还包罗语言国际化、图片上传等拍卖代码,如下所示

function initEditor() {
    $('#Note').summernote({
        lang: 'zh-CN',       // default: 'en-US'
        height: 600,         // set editor height                
        minHeight: null,    // set minimum height of editor
        maxHeight: null,    // set maximum height of editor
        focus: true,         // set focus to editable area after initializing summe
        callbacks: {
            onImageUpload: function (files) { //the onImageUpload API  
                img = sendFile(files[0]);
            }
        }
    });
}

4)、获取内容

var markupStr = $('#summernote').summernote('code');

 

5)、设置情节

设置情节和收获内容相近,在code后边伸张内需写入的HTML内容即可。

var markupStr = 'hello world';
$('#summernote').summernote('code', markupStr);

 

2.1 Block 数据结构简单认识

block的数据结构定义如下:

图片 1

相应的结构体定义如下:

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实例实际上由6局地构成:

  1. isa 指针,所有目标都有该指针,用于落到实处目标相关的机能。
  2. flags,用于按bit位表示一些block的附加音信,本文后边介绍 block copy
    的落实代码可以看来对该变量的施用
  3. reserved 保留变量
  4. invoke 函数指针,指向具体的block 完毕的函数调用地址
  5. descriptor 代表该block的附加描述新闻,紧即使size大小,以及 copy 和
    dispose 函数的指针。
  6. variables , capture
    过来的变量,block可以访问它表面的部分变量,就是因为将这几个变量(或变量的地方)复制到了结构体中。

在 OC 语言中,一共有 3 种档次的 block:

  1. _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
  2. _NSConcreteStackBlock 保存在栈中的 block,当函数再次来到时会被销毁
  3. _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0
    时会被销毁。

相见一个Block,大家怎么确定那些Block的囤积地点吗?

a。Block不访问外界变量(包涵栈中和堆中的变量)

Block既不在栈又不在堆中,在代码段中,ARC和MRC都是那般,此时为全局块。

b。Block访问外界变量

MRC 环境下:访问外界变量的Block默认存储在栈中。

ARC
环境下:访问外界变量的Block默许存储在堆中(实际是身处栈区,然后ARC处境下自行又拷贝到堆区),自动释放。

2、 summernote插件在骨子里项目中的使用

地方介绍了正常 summernote插件的应用进度,一般景观下,大家使用它除了编辑HTML内容外,还索要对图片上传进行拍卖,以管教大家得以平素把文件上传来后台文件系统里面去,而不是把它们转换为Base64的编码放在页面内。

相似的伊始化代码如下所示。

        function initEditor() {
            $('#Note').summernote({
                lang: 'zh-CN',       // default: 'en-US'
                height: 600,         // set editor height                
                minHeight: null,    // set minimum height of editor
                maxHeight: null,    // set maximum height of editor
                focus: true,         // set focus to editable area after initializing summe
                callbacks: {
                    onImageUpload: function (files) { //the onImageUpload API  
                        img = sendFile(files[0]);
                    }
                }
            });
        }

里面的sendFile的函数如下所示,紧如若调用附件管理模块举行文件的储存。

        //提交文件到服务器处理
        function sendFile(file) {
            data = new FormData();
            data.append("file", file);
            //增加额外的参数
            data.append("folder", '商品信息');
            data.append("guid", $("#ID").val());

            $.ajax({
                data: data,
                type: "POST",
                url: "/FileUpload/Upload",
                cache: false,
                contentType: false,
                processData: false,
                success: function (json) {
                    var data = $.parseJSON(json);
                    var url = data.urls[0];
                    $("#Note").summernote('insertImage', url, 'image name'); // the insertImage API  
                }
            });
        }

诚如的话,大家的文件为了在四个利用之间共享处理,一般提出以FTP方式开展处理,那地方可以参考小说《在附件管理模块中加进对FTP
上传和预览的支撑
》,FTP上传文件可以利用FluentFTP那个组件(GitHub地址:https://github.com/hgupta9/FluentFTP ),那个是一个用到很广,功效很强大的FTP组件。

上传附件到服务器上的后台控制器代码如下所示,首如若构建文件新闻,上传后返回对应的URL,然后就足以在 summernote插件上突显图片了。

        /// <summary>
        /// 上传附件到服务器上
        /// </summary>
        /// <param name="fileData">附件信息</param>
        /// <param name="guid">附件组GUID</param>
        /// <param name="folder">指定的上传目录</param>
        /// <returns></returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Upload(string guid, string folder)
        {
            //如果需要修改字段显示,则参考下面代码处理
            dynamic obj = new ExpandoObject();
            List<string> urls = new List<string>();
            var result = new CommonResult();

            HttpFileCollectionBase files = HttpContext.Request.Files;
            if (files != null)
            {
                foreach (string key in files.Keys)
                {
                    try
                    {
                        #region MyRegion
                        HttpPostedFileBase fileData = files[key];
                        if (fileData != null)
                        {
                            HttpContext.Request.ContentEncoding = Encoding.GetEncoding("UTF-8");
                            HttpContext.Response.ContentEncoding = Encoding.GetEncoding("UTF-8");
                            HttpContext.Response.Charset = "UTF-8";

                            string fileName = Path.GetFileName(fileData.FileName);      //原始文件名称
                            string fileExtension = Path.GetExtension(fileName);         //文件扩展名

                            FileUploadInfo info = new FileUploadInfo();
                            info.FileData = ReadFileBytes(fileData);
                            if (info.FileData != null)
                            {
                                info.FileSize = info.FileData.Length;
                            }
                            info.Category = folder;
                            info.FileName = fileName;
                            info.FileExtend = fileExtension;
                            info.AttachmentGUID = guid;

                            info.AddTime = DateTime.Now;
                            info.Editor = CurrentUser.Name;//登录人
                            //info.Owner_ID = OwerId;//所属主表记录ID

                            result = BLLFactory<FileUpload>.Instance.Upload(info);
                            if (!result.Success)
                            {
                                LogTextHelper.Error("上传文件失败:" + result.ErrorMessage);
                            }
                            else
                            {
                                //返回具体路径地址
                                string serverRealPath = info.BasePath.UriCombine(info.SavePath);
                                if (!Path.IsPathRooted(info.BasePath) &&
                                    !info.BasePath.StartsWith("http://") &&
                                    !info.BasePath.StartsWith("https://"))
                                {
                                    //如果是相对目录,加上当前程序的目录才能定位文件地址
                                    var url = HttpContext.Request.Url;
                                    var baseurl = url.AbsoluteUri.Replace(url.PathAndQuery, "");
                                    serverRealPath = baseurl.UriCombine(serverRealPath).Replace('\\', '/');
                                }
                                urls.Add(serverRealPath);
                            }
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        result.ErrorMessage = ex.Message;
                        LogTextHelper.Error(ex);
                    }
                }
                obj.urls = urls;
            }
            else
            {
                result.ErrorMessage = "fileData对象为空";
            }

            var newResult = new { Success = result.Success, ErrorMessage = result.ErrorMessage, urls = urls };
            return ToJsonContent(newResult);
        }

界面效果如本人在随笔介绍的一样《微信小程序结合后台数据管理落到实处货物数量的动态浮现、维护》。

案例对货物的详细音信的富文本举办编辑,来拓展图片文字的编排处理,如下界面所示。

图片 2

当大家插入图片的时候,弹出一个对话框界面,拔取文件上传后重临URL突显在SummerNote插件上。

 图片 3

上传文件成功后,通过下边的代码插入一个图纸,如下代码。

    $.ajax({
        data: data,
        type: "POST",
        url: "/FileUpload/Upload",
        cache: false,
        contentType: false,
        processData: false,
        success: function (json) {
            var data = $.parseJSON(json);
            var url = data.urls[0];
            $("#Note").summernote('insertImage', url, 'image name'); // the insertImage API  
        }
    });

 以上就是本身在其实的Bootstrap框架项目中行使
summernote插件已毕HTML文档的编撰和图表插入操作,全体性仍旧比较易于上手的,经验供我们借鉴。

 其余相关小说能够参照阅读下:

组成bootstrap
fileinput插件和Bootstrap-table表格插件,完结公文上传、预览、提交的导入Excel数据操作流程

基于Metronic的Bootstrap开发框架经验总计(16)–
使用插件bootstrap-table完毕表格记录的查询、分页、排序等拍卖

据悉Metronic的Bootstrap开发框架经验计算(15)– 更新使用Metronic
4.75版本

从开发框架提升开发功能说起 

根据Metronic的Bootstrap开发框架经验总括(14)–条码和二维码的变动及打印处理

依照Metronic的Bootstrap开发框架经验计算(13)–页面链接收藏夹作用的兑现2

据悉Metronic的Bootstrap开发框架经验计算(12)–页面链接收藏夹功效的兑现

据悉Metronic的Bootstrap开发框架经验总计(11)–页面菜单的二种表现形式 

基于Metronic的Bootstrap开发框架经验总括(10)–优化Bootstrap图标管理 

在MVC控制器里面使用dynamic和ExpandoObject,完结数量转义的出口

依照Metronic的Bootstrap开发框架经验计算(9)–已毕Web页面内容的打印预览和封存操作

依照Metronic的Bootstrap开发框架经验总括(8)–框架作用完全界面介绍

根据Metronic的Bootstrap开发框架经验计算(7)–数据的导入、导出及附件的查阅处理 

依照Metronic的Bootstrap开发框架经验计算(6)–对话框及提示框的拍卖和优化

根据Metronic的Bootstrap开发框架经验统计(5)–Bootstrap文件上传插件File
Input的利用

根据Metronic的Bootstrap开发框架经验计算(4)–Bootstrap图标的领到和行使

据悉Metronic的Bootstrap开发框架经验总计(3)–下拉列表Select2插件的应用 

据悉Metronic的Bootstrap开发框架经验总括(2)–列表分页处理和插件JSTree的施用

据悉Metronic的Bootstrap开发框架经验计算(1)-框架总览及菜单模块的拍卖 

2.2 NSConcreteGlobalBlock 类型的 block 的实现

俺们可以新建一个block1.c文书:

#include <stdio.h>
int main()
{
    ^{ printf("Hello, World!\n"); } ();
    return 0;
}

 在终点输入 clang -rewrite-objc block1.c ,就可以在目录中看看 clang
输出了一个 block1.cpp 的公文,这些文件就是 block 在 C 语言的完毕:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    printf("Hello, World!\n");
}
static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) };
int main()
{
    (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA) ();
    return 0;
}
  1.   一个block实际就是一个对象,它紧要由一个 isa 和一个 impl 和一个
    descriptor 组成。
  2. 那里大家看出 isa 指向的依然 _NSConcreteStackBlock,但在 LLVM
    的落到实处中,开启 ARC 时,block 应该是 _NSConcreteGlobalBlock
    类型。感觉是当一个 block 被声称的时候,它都是一个
    _NSConcreteStackBlock类的靶子。
  3. impl 是事实上的函数指针,本例中,它指向 _main_block_func_0。那里的
    impl 约等于事先涉嫌的 invoke 变量,只是 clang
    编译器对变量的命名不均等。
  4. descriptor 是用于描述当前那个 block
    的叠加音信的,包涵结构体的大小,须求 捕获 和 处理
    的变量列表等。结构体大小必要保留是因为,每个 block 因为会 捕获
    一些变量,那几个变量会加到 __main_block_impl_0
    这一个结构体中,让其体积变大。前面会看到相关代码。

2.3 NSConcreteStackBlock 类型的 block 的实现

俺们此外新建一个名为 block2.c 的公文,输入一下情节:

#include <stdio.h>
int main() {
    int a = 100;
    void (^block2)(void) = ^{
        printf("%d\n", a);
    };
    block2();
    return 0;
}

 再一次使用 clang 工具,转换后的重大代码如下:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int a;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int a = __cself->a; // bound by copy
    printf("%d\n", a);
}
static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main()
{
    int a = 100;
    void (*block2)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a);
    ((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
    return 0;
}

  在本例中,大家得以看到:

  1. 本例中,isa 指向
    _NSConcreteStackBlock,表达那是一个分红在栈上的实例。
  2. main_block_impl_0
    中加进了一个变量a,在block中援引的变量a实际上是在表明block时,被复制到
    main_block_impl_0
    结构体中的那么些变量a。y因为这么,大家就能明了,在block内部修改变量a的始末,不会潜移默化外部的实际上变量a。
  3. main_block_impl_0
    中由于增加了一个变量a,所以结构体的大小变了,该结构体大小被写在了
    main_block_desc_0 中。

大家修改上边的代码,在变量前边增添 __block 关键字:

#include <stdio.h>
int main()
{
    __block int i = 1024;
    void (^block1)(void) = ^{
        printf("%d\n", i);
        i = 1023;
    };
    block1();
    return 0;
}

  生成的基本点代码如下,可以看看,差距很大:

struct __Block_byref_i_0 {
    void *__isa;
    __Block_byref_i_0 *__forwarding;
    int __flags;
    int __size;
    int i;
};
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_i_0 *i; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__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_i_0 *i = __cself->i; // bound by ref
    printf("%d\n", (i->__forwarding->i));
    (i->__forwarding->i) = 1023;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main()
{
    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 1024};
    void (*block1)(void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344);
    ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
    return 0;
}

  从代码中大家得以见到:

  1. 源码中追加一个名为 __block_byref_i_0 的结构体,用来保存大家要
    捕获 并且修改的变量 i。
  2. main_block_impl_0 引用的是 Block_byref_i_0
    的结构体指针,那样就可以高达修改外部变量的作用。
  3. __Block_byref_i_0 结构体中蕴藏 isa,表明它也是一个对象。
  4. 俺们需求承受 Block_byref_i_0 结构体相关的内存管理,所以
    main_block_desc_0 中追加了 copy 和 dispose
    函数指针,对于在调用前后修改响应变量的引用计数。

缘何选用__block 修饰的外表变量的值就足以被block修改呢?

咱俩发现一个有的变量加上 __block
修饰符后竟是跟block一样成为了一个__Block_byref_i_0结构体类型的电动变量实例。此时大家在block内部访问
i 变量则需求经过一个叫 __forwarding 的分子变量来直接访问 i 变量。

__block 变量和 __forwarding

在copy操作之后,既然__block变量也被copy到堆上去了,那么访问该变量是造访栈上仍然堆上的吗?

图片 4

通过__forwarding, 无论是在block中如故 block外访问__block变量,
也不管该变量在栈上或堆上, 都能胜利地访问同一个__block变量。

2.3 NSConcreteMallocBlock 类型的 block 的实现

NSConcreteMallocBlock 类型的 block
平常不会在源码中直接出现,因为默许它是当一个 block 被 copy
的时候,才会将以此 block 赋值到堆中。以下是一个 block 被copy
时的演示代码,可以看看,在第8步,目的的 block 类型被修改为
_NSConcreteMallocBlock。

static void *_Block_copy_internal(const void *arg, const int flags) {
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
    // 1
    if (!arg) return NULL;
    // 2
    aBlock = (struct Block_layout *)arg;
    // 3
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    // 4
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }
    // 5
    struct Block_layout *result = malloc(aBlock->descriptor->size);
    if (!result) return (void *)0;
    // 6
    memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    // 7
    result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
    result->flags |= BLOCK_NEEDS_FREE | 1;
    // 8
    result->isa = _NSConcreteMallocBlock;
    // 9
    if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
        (*aBlock->descriptor->copy)(result, aBlock); // do fixup
    }
    return result;
}

3. 变量的复制

对此 block
外的变量引用,block默许是将其复制到其数据结构中来贯彻访问的,也就是说block的自行变量只针对block内部使用的全自动变量,不行使则不截获,因为截获的自发性变量会蕴藏于block的结构体内部,会导致block体积变大,默许情状下
block 只可以访问不可能修改部分变量的值,如下图所示:

图片 5

对于 __block 修饰的表面变量引用,block
是复制其引述地址来落实访问的,block能够修改__block
修饰的外表变量的值,如下图所示:

图片 6

 

4. ARC 对 block 类型的熏陶

在 ARC 开启的情状下,将只会有 NSConcreteGlobalBlock 和
NSConcreteMallocBlock 类型的 block。

原先的 NSConcreteStackBlock 的 block 会被 NSConcreteMallocBlock 类型的
block替代。注脚格局是以下代码再 XCode 中,会输出
<__NSMallocBlock__: 0x100109960>。

在苹果的合法文档中也涉及,当把栈中的block重回时,不必要调用 copy
方法了。

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        int i = 1024;
        void (^block1)(void) = ^{
            printf("%d\n", i);
        };
        block1();
        NSLog(@"%@", block1);
    }
    return 0;
}

 ARC下,访问外界变量的 Block 为何要从栈区拷贝到堆区呢?

栈上的Block,假如其所属的变量成效域甘休,该Block就被撤销,如同一般的机动变量。当然,Block中的__block变量也还要被抛弃:

图片 7

为通晓决栈块在其变量功效域停止之后被放弃(释放)的问题,我们需求把Block复制到堆中,延长其生命周期。开启ARC时,大多数动静下编译器会恰当地进行判断是或不是有亟待将Block从栈复制到堆,假诺有,自动生成将Block从栈上复制到堆上的代码。Block的复制操作实施的是copy实例方法。Block只要调用了copy方法,栈块就会化为堆块。

如下图:

图片 8

5. 链式语法的兑现

  类似于第三方自动布局 梅森ry 的代码:

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

5.1 怎样促成

我们举个例子,如果对于一个已有类的实例 classInstance,现在要用句点 .
和小括号 () 的主意连接调用它的”方法”
method1,method2,method3,如下图所示:

图片 9

从图中我们可以知道,要促成链式语法,首要包蕴点语法、小括号调用、三番五次访问 三有些:

  • 点语法:在OC中,对于点语法的利用,最常见于属性的拜访,比如对在章程内部调用
    self.xxx,在类的实例中用 classInstance.xxx;
  • 小括号调用:OC中貌似用中括号 [] 来达成格局的调用,而对于 Block
    的调用则仍旧封存使用小括号 ( ) 的艺术,由此我们能够考虑用
    Block来贯彻链式语法中的 ();
  • 怎么着贯彻连接走访?:Block可以领略为涵盖自动变量的匿名函数或函数指针,它也是有再次来到值的。大家得以把上述类实例每一遍方法的调用(实质为
    Block 的调用)的再次回到值都设为当前类实例本身,即
    classInstance.method1() 重回了脚下 classInstance
    ,此时才能在其背后继续执行 .method2() 的调用,以此类推。

小结一句话:大家可以定义类的一对只读 Block 类型的性能,并把那一个 Block
的回到值类型设置为当下类本身,然后已毕这几个 Block 属性的 getter 方法。

下边是一个Demo,链式计算器的事例,可以接连地调用统计器的加减乘除进行总结:

@interface Cacluator : NSObject

@property (assign, nonatomic) NSInteger result;

// 下面分别定义加减乘除四个只读block类型的属性
// 设置为只读是为了限制只需要实现 getter方法
// 这里每个 Block 类型的属性携带一个 NSInteger 类型的参数,返回参数是当前类型
@property (copy, nonatomic, readonly) Cacluator *(^add)(NSInteger number);
@property (copy, nonatomic, readonly) Cacluator *(^minus)(NSInteger number);
@property (copy, nonatomic, readonly) Cacluator *(^multiply)(NSInteger number);
@property (copy, nonatomic, readonly) Cacluator *(^divide)(NSInteger number);

@end


@implementation Cacluator

// 此处为 add 属性的 getter方法实现
// 前面声明 add 属性的类型为 block 类型,所以此处 getter 返回一个 block
// 对于返回的 block,返回值类型为 Calculator,所以返回self

-(Cacluator *(^)(NSInteger))add{
    return ^id(NSInteger num){
        self.result += num;
        return self;
    };
}

-(Cacluator *(^)(NSInteger))minus{
    return ^id(NSInteger num){
        self.result -= num;
        return self;
    };
}

-(Cacluator *(^)(NSInteger))multiply{
    return ^id(NSInteger num){
        self.result *= num;
        return self;
    };
}

-(Cacluator *(^)(NSInteger))divide{
    return ^id(NSInteger num){
        NSAssert(num != 0, @"除数不能为0");
        self.result /= num;
        return self;
    };
}

@end

 测试代码:

Calculator *calc = [[Calculator alloc] init]; // 初始化一个计算器类实例

calc.add(8).minus(4).multiply(6).divide(3); // 链式调用

NSLog(@"%d", (int)calc.result); // 输出 8

 分析:

上面 calc.add 访问 calc 的 add 属性会调用 [calc add] 方法,此方法会返回一个Block如下:

^id(NSInteger num){
      self.result += num;
      return self;  
};

在这个Block中,前面已声明其返回值类型为:Caculator,所以在其里面返回了 self,这样当调用该 Block 时,会返回 self (实例本身),流程如下:

1.calc.add 获得一个 Block
2.calc.add(8) Block 的执行,并返回了 self (即实例 calc)
3.于是在 calc.add(8) 后面可继续访问 calc 的其他属性,一路点下去

 5.2 更简单的落成

下面是透过先声惠氏(WYETH)层层的Block属性, 再去完结Block属性的getter
方法来落实链式调用,感觉仍然多少劳顿,大家去看看是或不是有更简短的贯彻方式:

图片 10

点语法的本色:

  • 在OC中,点语法实际上只是一种替换手段,对于属性的getter方法,class.xxx
    的写法最后会被编译器替换成 [class xxx];对于setter 方法,即把
    class.xxx 写在等号左侧,class.xxx = value 会被更换成 [class
    setXxx:value],本质都是措施调用
  • 即使再class中并不曾显式注解 xxx 属性,在编译代码时,代码中假使有
    class.xxx 的写法也会被轮换成 [class
    xxx],所以如若在class中有一个宣称为 xxx
    的措施,即可在代码中其它地方写 class.xxx

为此,解决方案是:

  在定义类的头文件的@interface中,直接声明某一艺术名为xxx,该方法的再次回到值是一个Block,而此block的回到值设为此类本身。

@interface Calculator : NSObject

@property (nonatomic, assign) NSInteger result; // 保存计算结果

// 上面的属性声明其实是可以省略的,只要声明下面方法即可;
// 在 Objective-C 中,点语法只是一种替换手段,class.xxx 的写法(写在等号左边除外)最终会被编译器替换成 [class xxx],本质上是方法调用;

// add、minus、multiply、divide 四个方法都会返回一个 Block,
// 这个 Block 有一个 NSInteger 类型的参数,并且其返回值类型为当前 Calculator 类型;
// 下面四个方法的实现与上面 Calculator.m 中的一致。
- (Calculator * (^)(NSInteger num)) add;
- (Calculator * (^)(NSInteger num)) minus;
- (Calculator * (^)(NSInteger num)) multiply;
- (Calculator * (^)(NSInteger num)) divide;

 梅森ry
也是这么做的,只申明了点子,并不曾讲明相应的属性。此外,对于Masonry链式语法中的
.and、.with 等写法只是为着让代码读起来更通畅,达成方式为:注明一(Wissu)个名为
and 和 with 的法子,在章程里重回self:

- (MASConstraint *)with {
    return self;
}

- (MASConstraint *)and {
    return self;
}

 存在的问题:

当用点语法去访问类的某一个 Block 属性时,Block 前边的参数 Xcode

XXXHTTPManager *http = [XXXHTTPManager manager];

// 下面 .get(...) 里面的参数,Xcode 并不会提示自动补全,需要手动去填写,.success(...) .failure(...) 等也一样,
// 这里不能像传统中括号 [] 方法调用那样,输入方法名就可以自动提示该方法所有的参数并按回车自动补全。
http.get(@"https://kangzubin.cn", nil).success(^(NSURLSessionDataTask *task, id responseObject) {
    // Success TODO
}).failure(^(NSURLSessionDataTask *task, NSError *error) {
    // Failure TODO
}).resume();

 Xcode 中有个有力但未被充足利用的职能:Code
Snippets(代码块)可以解决。

http://www.imlifengfeng.com/blog/?utm\_medium=email&utm\_source=gank.io&p=457

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注