小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

理解Objective

 最初九月雪 2017-05-06

所有文章均為作者原創(chuàng),轉(zhuǎn)載請注明出處

最近看了一篇很有趣文章,關(guān)于block的quiz,5道題答錯了兩道。之前理解block可以在heap和stack上alloc,看了這篇文章才發(fā)現(xiàn)原來還有g(shù)lobal的block。想想也是,這種block不引用任何variable,在編譯的時候就可以確定下來了。

最開始用block的時候,感到很疑惑,原因是這種用法從編程思想上來講屬于functional programming。不符合面向?qū)ο蟮脑O計原則,但是萬物無絕對,就像在面向?qū)ο蟮氖澜缰型瑯佑袉为毚嬖诘暮瘮?shù)一樣,現(xiàn)代編程語言已經(jīng)不拘泥于某種特定的形式。而熟悉Objective-C的人應該知道,這門語言是從Lisp演進過來,而Lisp是最早的函數(shù)型編程語言,這篇文章寫的很不錯:《Functional Programming Is Hard, That’s Why It’s Good》。關(guān)于Lisp,一直沒有精力來仔細研究一下,略感遺憾。后面的smallTalk保留了Lisp的這一特性,進而帶到了Objective-C中,所以在OC中出現(xiàn)block也并不奇怪。

matt Galloway關(guān)于block做了很詳細的分析,并給出了LLVM中關(guān)于Block的定義。但是紙上得來終覺淺,為了理解的更透徹,我們還是親自來debug一下,我們就以ExampleA為例:

Example A

為了分析方便,我們把Example A稍微改寫一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void exampleA() {
  char a = 'A';

  void(^block)() =
  ^{
    printf("%c\n", a);
  };

  block();
}

int main(int argc, char *argv[]) {
	
	exampleA();
	return 0;
}

我們可以通過clang來rewrite這段代碼:clang -rewrite-objc test.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#line 4 "test.c"
struct __exampleA_block_impl_0 {
  struct __block_impl impl;
  struct __exampleA_block_desc_0* Desc;
  char a;
  __exampleA_block_impl_0(void *fp, struct __exampleA_block_desc_0 *desc, char _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

#line 8 "test.c"
static void __exampleA_block_func_0(struct __exampleA_block_impl_0 *__cself) {
  char a = __cself->a; // bound by copy

    printf("%c\n", a);
  }

static struct __exampleA_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __exampleA_block_desc_0_DATA = { 0, sizeof(struct __exampleA_block_impl_0)};

#line 4 "test.c"
void exampleA() {
  char a = 'A';

  void(*block)() =
  (void (*)())&__exampleA_block_impl_0((void *)__exampleA_block_func_0, &__exampleA_block_desc_0_DATA, a);

  ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}


#line 15 "test.c"
int main(int argc, char *argv[]) {

 exampleA();
 return 0;
}
}

需要指出的一點是,這個rewrite-objc的命令很詭異,我們的預期是將一個.c文件僅在語法層面將block展開,生成另一個.c文件。但是實際上這個命令生成的是一個.cpp的C++文件,這意味著它在block的結(jié)構(gòu)體中插入了構(gòu)造函數(shù):

1
2
3
4
5
6
__exampleA_block_impl_0(void *fp, struct __exampleA_block_desc_0 *desc, char _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }

不知道clang為什么要把它rewrite成C++的。但這并不影響我們閱讀代碼:

  • main函數(shù)執(zhí)行exampleA函數(shù)

  • 在exampleA函數(shù)中,首先通過構(gòu)造函數(shù)創(chuàng)建一個block:由于這個block是在exampleA的stack中,所以block的isa類型為_NSConcreteStackBlock,那么當exampleA執(zhí)行完畢后,block就會被銷毀掉;block有自己的內(nèi)存空間來保存?zhèn)鬟M來的參數(shù)a;block的回調(diào)函數(shù)指向了__exampleA_block_func_0

  • 執(zhí)行block的回調(diào)函數(shù):在__exampleA_block_func_0中,輸出的a實際上是block結(jié)構(gòu)體中的a,不是block外部的a,因此,如果在__exampleA_block_func_0內(nèi)部改變a的值,改變不是外部的a值。

__block

如果我們想改變a的值,通常要把a聲明成__block:

1
2
3
4
5
6
7
8
9
10
11
12
void exampleA() {

  __block char a = 'A';

  void(^block)() =
  ^{
	a = 'B';
    printf("%c\n", a);
  };

  block();
}

這樣我們就能改變a的值了

why?

我們再次使用:clang -rewrite-objc test.c,看看有什么不同的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 char a;
};

struct __exampleA_block_impl_0 {
  struct __block_impl impl;
  struct __exampleA_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __exampleA_block_impl_0(void *fp, struct __exampleA_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;
  }
};

#line 9 "test.c"
static void __exampleA_block_func_0(struct __exampleA_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref

 (a->__forwarding->a) = 'B';
    printf("%c\n", (a->__forwarding->a));
  }
static void __exampleA_block_copy_0(struct __exampleA_block_impl_0*dst, struct __exampleA_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __exampleA_block_dispose_0(struct __exampleA_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __exampleA_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __exampleA_block_impl_0*, struct __exampleA_block_impl_0*);
  void (*dispose)(struct __exampleA_block_impl_0*);
} __exampleA_block_desc_0_DATA = { 0, sizeof(struct __exampleA_block_impl_0), __exampleA_block_copy_0, __exampleA_block_dispose_0};

#line 4 "test.c"
void exampleA() {

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

  void(*block)() =
  (void (*)())&__exampleA_block_impl_0((void *)__exampleA_block_func_0, &__exampleA_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344);

  ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
}
  • exampleA發(fā)生了變化,產(chǎn)生了一個新的結(jié)構(gòu)體:__Block_byref_a_0

  • 原來是傳a的值,現(xiàn)在變成了傳a的引用。

Closure

實際應用中,我們并不需要了解block是如何實現(xiàn)的,但是理解block后面的思想?yún)s很關(guān)鍵。前面已經(jīng)提到過Functional Programming的概念,F(xiàn)unctional Programming中一個很重要的概念是Closure,翻譯過來叫“閉包”。它有幾個最基本的feature:

(1)First-Class Value:函數(shù)也是一種變量,也可以像變量一樣被賦值,被當做參數(shù)傳遞和返回。 (2)High Order Function:高階函數(shù),即一個函數(shù)的參數(shù)也可以是另一個函數(shù),并且這個函數(shù)可以是匿名函數(shù)。 (3)Lexical Scoping:靜態(tài)作用域,也叫詞法作用域,解釋起來很麻煩,感興趣的可以看維基百科。

這三條是不是很熟悉,沒錯,Objective-C中的block這幾條都支持:

1
2
void(^func)(int x, int y) = ^(int x,int y){ NSLog(@"%d,%d",x,y);};
    func(100,100);

這說明block可以和普通變量一樣,進行定義和賦值

1
2
3
4
5
 __block int i = 0;
int a = newCounter( ^
        i = i+1;
        return i;
    });

這說明,block可以作為匿名函數(shù)直接作為參數(shù)傳遞,同時它還可以訪問i,說明block也支持lexical scoping。

其實所謂block僅僅是語法層面的東西,任何支持closure的語言都有它獨特的一套語法和形式,尤其對于腳本語言,沒有強類型的限制,使用起來更靈活,更好玩,例如,Lua的table.sort方法中第二個參數(shù):

1
2
3
4
5
6
7
8
network = {
	{name="jason",ip = "192.168.1.11"},
	{name="kate",ip = "192.168.1.12"},
	{name="candy",ip = "192.168.1.13"},
	{name="carl",ip = "192.168.1.14"},
}
table.sort(network,function(a,b) return(a.name > b.name) end)
print(network[1].name,network[1].ip) -->kate,192.168.1.12

因此,總結(jié)來說,對于block,僅僅是Objective-C實現(xiàn)Closure的一種途徑,為的是在某些場合用起來更方便,使代碼更簡潔,更緊湊而已。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多