iOS – Block

  说交PHP代码的优化,PHP开发之有点技巧我想许多总人口且发出和好的如出一辙模仿,下面分享部分有些技巧,希望对大家所有助。

1. Block

  1、循环中并非声明变量,尤其是目标这样的变量。

1.1 什么是Block

  之前还是对block的简练实用,这里还了解下。

  代码块Block是苹果于iOS4始发引入的对C语言的壮大,实现匿名函数的特性,Block是同一种特有之数据类型,其可以正常定义变量、作为参数、作为返回值,特殊的,block还好保留一截代码,在急需之时光调用,目前Block广泛的使iOS开发中,常用来GCD、动画、排序和各类回调。

  注:Block的宣示与赋值只是保存了平截取代码段,必须调用才会实施中的代码。  

  2、foreach效率更强,尽量用foreach代替while和for循环。

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();

  3、循环里转用函数

2. Block 数据结构

       for($x=0; $x < count($array); $x)

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情况下活动又拷贝到堆区),自动释放。

       //count()函数在外面先计算

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
    这个结构体中,让那个体积变充分。后面会盼相关代码。

  4、用i+=1代替i=i+1。符合c/c++的惯,效率还高。

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到堆上了,那么看该变量是看栈上还是堆上的吧?

图片 2

通过__forwarding, 无论是以block中尚是 block外访问__block变量,
也随便该变量在栈上或堆上, 都能够无往不利地访问与一个__block变量。

  5、优化Select
SQL语句,在恐的事态下尽可能少之展开Insert、Update操作,达到PHP性能优化的目的。

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;
}

  6、尽量的丢进行文件操作,虽然PHP的文书操作效率也无逊色的。

3. 变量的复制

对此 block
外的变量引用,block默认是以那复制到那数据结构中来实现访问的,也就是说block的自发性变量只针对block内部采用的机动变量,不行使则免缴,因为截获的机关变量会储存于block的结构体内部,会招block体积变大,默认情况下
block 只能访问不能修改部分变量的价值,如下图所示:

图片 3

对于 __block 修饰的表变量引用,block
是复制该引述地址来实现访问的,block可以修改__block
修饰的表变量的价值,如下图所示:

图片 4

 

  7、在得就此PHP内部字符串操作函数的状态下,不要用正则表达式。

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变量也以为废弃:

图片 5

为缓解栈块在该变量作用域结束以后吃丢(释放)的题目,我们要拿Block复制到堆着,延长其生命周期。开启ARC时,大多数景下编译器会恰当地开展判断是否发生得将Block从栈复制到堆,如果发生,自动生成将Block从栈上复制到堆上的代码。Block的复制操作实践之是copy实例方法。Block只要调用了copy方法,栈块就会成堆块。

如下图:

图片 6

       // GOOD

5. 链式语法的落实

  类似于第三正值自动布局 Masonry 的代码:

[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);
}];

       $addr = strtr($addr, “abcd”, “efgh”);

5.1 如何贯彻

咱俩举个例子,假如对于一个业已有类的实例 classInstance,现在只要就此句点 .
和小括号 () 的不二法门连接调整用她的”方法”
method1,method2,method3,如下图所示:

图片 7

由图被我们好知晓,要促成链式语法,主要涵盖
点语法、小括号调用、连续访问 三有的:

  • 点语法:每当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 的其他属性,一路点下去

       // BAD

 5.2 更精简的贯彻

地方是经事先声明一多样的Block属性, 再错过贯彻Block属性的getter
方法来实现链式调用,感觉要稍烦,我们去看是不是发再次简单之落实方式:

图片 8

点语法的原形:

  • 当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;

 Masonry
也是这么做的,只声明了章程,并不曾声明相应的性能。另外,对于Masonry链式语法中之
.and、.with 等写法只有是以给代码读起来还通畅,实现方式啊:声明一个誉为也
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(代码块)可以解决。

iOS Block详解

       $addr = strtr($addr, array(‘a’ => ‘e’, ));

       ?>

  8、在好为此file_get_contents替代file、fopen、feof、fgets等名目繁多措施的气象下,尽量用file_get_contents,因为它们的频率高得几近。但是若顾file_get_contents在开拓一个URL文件上的PHP版本问题。

  9、Apache解析一个PHP脚本的日子如于解析一个静态HTML页面慢2暨10倍增。尽量多为此静态HTML页面,少用剧本。

  10、使用选择分语句,switch case好为以多独if,else
if语句,并且代码更加便于看和护卫。

  11、当echo字符串时用逗号代替点连接符更快来。echo一栽可以管多个字符串当作参数的“函数”。echo是言语结构,不是真的的函数,故将函数加上了双双引号。例如:echo
$str1,$str2

发表评论

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

网站地图xml地图