最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

iOS底層實(shí)例解析Swift閉包及OC閉包

 更新時(shí)間:2022年11月15日 10:41:52   作者:Yakamoz  
這篇文章主要為大家介紹了iOS底層實(shí)例解析Swift閉包及OC閉包,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

基礎(chǔ)

Block是?個(gè)自包含的(捕獲了上下?的常量或者是變量的)函數(shù)代碼塊,可以在代碼中被傳遞和使用。

全局和嵌套函數(shù)實(shí)際上也是特殊的閉包,閉包采用如下三種形式之一:

  • 全局函數(shù)是一個(gè)有名字但不會(huì)捕獲任何值的閉包
  • 嵌套函數(shù)是一個(gè)有名字并可以捕獲其封閉函數(shù)域內(nèi)值的閉包
  • 閉包表達(dá)式是一個(gè)利用輕量級(jí)語(yǔ)法所寫的可以捕獲其上下文中變量或常量值的匿名閉包

OC-Block

分類

NSGlobalBlock

  • 位于全局區(qū)
  • 在Block內(nèi)部不使用外部變量,或者只使用靜態(tài)變量和全局變量

NSMallocBlock

  • 位于堆區(qū)
  • 被強(qiáng)持有
  • 在Block內(nèi)部使用局部變量或OC屬性,可以賦值給強(qiáng)引用/copy修飾的變量

NSStackBlock

  • 位于棧區(qū)
  • 沒有被強(qiáng)持有
  • 在Block內(nèi)部使用局部變量或OC屬性,不能賦值給強(qiáng)引用/copy修飾的變量

如下簡(jiǎn)單demo code所示

int a = 10; // 局部變量
void(^Global)(void) = ^{
    NSLog(@"Global");
};
void(^Malloc)(void) = ^{
    NSLog(@"Malloc,%d",a);
};
void(^__weak Stack)(void) = ^{
    NSLog(@"Stack,%d",a);
};
NSLog(@"%@",Global); // <__NSGlobalBlock__: 0x101aa80b0>
NSLog(@"%@",Malloc); // <__NSMallocBlock__: 0x600003187900>
NSLog(@"%@",Stack); // <__NSStackBlock__: 0x7ff7b12c22f0>

下面重點(diǎn)介紹堆Block。

NSMallocBlock

Block拷貝到堆Block的時(shí)機(jī):

  • 手動(dòng)copy
  • Block作為返回值
  • 被強(qiáng)引用/copy修飾
  • 系統(tǒng)API包含using Block

所以總結(jié)一下堆Block判斷依據(jù):

  • Block內(nèi)部有沒有使用外部變量
  • 使用的變量類型?局部變量/OC屬性/全局變量/靜態(tài)變量
  • 有沒有被強(qiáng)引用/copy修飾

源碼探究

我們創(chuàng)建一個(gè)捕獲了局部變量的block

#import <Foundation/Foundation.h>
void test() {
    int a = 10;
    void(^Malloc)(void) = ^{
        NSLog(@"%d",a);
    };
}

執(zhí)行clang -rewrite-objc main.m -o main.cpp命令,查看main.cpp文件可以看到Malloc閉包的結(jié)構(gòu)如下。

struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
	// 內(nèi)部存儲(chǔ)了變量a
  int a;
	/// 初始化函數(shù)。包含三個(gè)參數(shù)
	// - Parameters:
  ///   - fp: 函數(shù)指針
  ///   - desc: 描述
  ///   - _a: flag
  __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// 創(chuàng)建Malloc閉包,傳入?yún)?shù)如下
// fp: (void *)__test_block_func_0
// desc: &__test_block_desc_0_DATA
// _a: 變量a的值(值拷貝)
void(*Malloc)(void) = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, a));
// __test_block_func_0實(shí)現(xiàn)如下
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy
			NSLog(···);
    }

打開llvm可以看到,該block原本是在棧上,調(diào)用了objc_retainBlock方法,而在該方法中實(shí)際調(diào)用了_Block_copy方法。

在Block.h的源碼中可以找到_Block_copy方法,其官方注釋是“創(chuàng)建一個(gè)基于堆的Block副本,或者簡(jiǎn)單地添加一個(gè)對(duì)現(xiàn)有Block的引用。”,從而將這個(gè)棧block拷貝到了堆上,下面我們根據(jù)該方法的源碼來探究一下堆Block的原理。(只截取重點(diǎn)代碼)

void *_Block_copy(const void *arg) {
    return _Block_copy_internal(arg, true);
}
static void *_Block_copy_internal(const void *arg, const bool wantsOne) {
    struct Block_layout *aBlock;
		···
		// 類型強(qiáng)轉(zhuǎn)為Block_layout
    aBlock = (struct Block_layout *)arg;
		···
    // Its a stack block.  Make a copy.
		// 分配內(nèi)存
		struct Block_layout *result = malloc(aBlock->descriptor->size);
		if (!result) return NULL;
		memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
		// reset refcount
		result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
		result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
		// isa重新標(biāo)記為Malloc Block
		result->isa = _NSConcreteMallocBlock;
		_Block_call_copy_helper(result, aBlock);
		return result;
}

Block底層結(jié)構(gòu)為Block_layout

struct Block_layout {
    void *isa;  // isa指針
    volatile int32_t flags; // contains ref count
    int32_t reserved; // 保留位
    void (*invoke)(void *, ...); // call out funtion
    struct Block_descriptor_1 *descriptor;
};

總結(jié):

Block在運(yùn)行時(shí)才會(huì)被copy,在堆上開辟內(nèi)存空間。

循環(huán)引用

解決方案

__weak + __strong

思路: 在block里短暫持有self的生命周期。(weak 自動(dòng)置空)

self.name = @"YK";
__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;
    strongSelf.callFunc();
};

__block

思路: 值拷貝。(手動(dòng)置空)

我們有如下代碼,生成cpp文件看一下

#import <Foundation/Foundation.h>
void test() {
    __block int a = 10;
    void(^Malloc)(void) = ^{
        a++;
        NSLog(@"%d",a);
    };
    Malloc();
}
// 可以看到傳入的第三個(gè)參數(shù),是__Block_byref_a_0結(jié)構(gòu)體類型的a變量地址,而不是上面講過的直接存儲(chǔ)int類型
void(*Malloc)(void) =
((void (*)())&__test_block_impl_0((void *)__test_block_func_0,
                                  &__test_block_desc_0_DATA,
                                  (__Block_byref_a_0 *)&a,
                                  570425344));
// __test_block_impl_0結(jié)構(gòu)體中存儲(chǔ)的變量也是__Block_byref_a_0類型
struct __test_block_impl_0 {
  struct __block_impl impl;
  struct __test_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __test_block_impl_0(void *fp, struct __test_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;
  }
};
// 初始化__Block_byref_a_0如下
__attribute__((__blocks__(byref))) __Block_byref_a_0 a =
{(void*)0,
        (__Block_byref_a_0 *)&a,
        0,
        sizeof(__Block_byref_a_0),
        10};
// __Block_byref_a_0結(jié)構(gòu)體
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding; // 指針指向原始值
 int __flags;
 int __size;
 int a; // 值拷貝存儲(chǔ)
};

總結(jié) __block 原理

  • 創(chuàng)建__Block_byref_a_0 結(jié)構(gòu)體
  • 傳給block指針地址
  • block內(nèi)修改的是與原始值同一片的內(nèi)存空間

注意點(diǎn)

根據(jù)上述分析我們可以得出結(jié)論,如果在OC的block中捕獲了沒有加__block 的外部變量,在編譯時(shí)就會(huì)將變量值傳入(值拷貝),如果捕獲了加__block 的外部變量,則會(huì)獲取到變量指針對(duì)應(yīng)的內(nèi)存空間的地址。代碼驗(yàn)證如下

int a = 1;
__block int b = 2;
void(^Malloc)(void) = ^{
    NSLog(@"a,%d",a);
    NSLog(@"b,%d",b);
};
a = 3;
b = 4;
Malloc();
// 輸出結(jié)果如下
// a,1
// b,4

Swift-Closure

  • Swift 的閉包表達(dá)式擁有簡(jiǎn)潔的風(fēng)格,并鼓勵(lì)在常見場(chǎng)景中進(jìn)行語(yǔ)法優(yōu)化,主要優(yōu)化如下:
    • 利用上下文推斷參數(shù)類型和返回值類型
    • 隱式返回單表達(dá)式閉包(單表達(dá)式閉包可以省略 return 關(guān)鍵字)
    • 參數(shù)名稱縮寫,可以用0,0,0,1表示
    • 尾隨閉包語(yǔ)法:如果函數(shù)的最后一個(gè)參數(shù)是閉包,則閉包可以寫在形參小括號(hào)的外面。為了增強(qiáng)函數(shù)的可讀性。
  • Swift 的閉包是一個(gè)引用類型,驗(yàn)證如下。我們知道Swift的引用類型在創(chuàng)建時(shí)都會(huì)調(diào)用swift_allocObject方法
// 未調(diào)用swift_allocObject
let closure1 = { () -> () in
    print("closure1")
}
// 調(diào)用swift_allocObject
let a = 10
let closure2 = { () -> () in
    print("closure2 \(a)")
}

捕獲值

  • 在閉包中如果通過[variable1, variabla2]的形式捕獲外部變量,捕獲到的變量為let類型,即不可變
  • 在閉包中如果直接捕獲外部變量,獲取的是指針,也就是說在閉包內(nèi)修改變量值的話,原始變量也會(huì)被改變。
  • 如果捕獲的是指針類型(Class),無(wú)論是否用[],在閉包內(nèi)對(duì)該變量進(jìn)行修改,都會(huì)影響到原始變量

簡(jiǎn)單驗(yàn)證如下:

var variable = 10
let closure = { () -> () in
    variable += 1
    print("closure \(variable)")
}
closure() // closure 11
print(variable) // 11

可見直接獲取變量的話,會(huì)修改到原始值。

如果改成下面這樣會(huì)編譯報(bào)錯(cuò)”可變運(yùn)算符的左側(cè)不可變”

var variable = 10
let closure = { [variable] () -> () in
    variable += 1
    print("closure \(variable)")
}
closure()
print(variable)

捕獲指針類型驗(yàn)證

class YKClass {
    var name = "old"
}
let demoS = YKStruct()
let demoC = YKClass()
let closure1 = { [demoC] () -> () in
    demoC.name = "new"
    print("closure1 \(demoC.name)")
}
closure1() // closure1 new
print(demoC.name) // new
let closure2 = { () -> () in
    demoC.name = "new2"
    print("closure2 \(demoC.name)")
}
closure2() // closure2 new2
print(demoC.name) // new2

以上就是iOS底層實(shí)例解析Swift閉包及OC閉包的詳細(xì)內(nèi)容,更多關(guān)于iOS底層Swift OC閉包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用設(shè)計(jì)模式中的Singleton單例模式來開發(fā)iOS應(yīng)用程序

    使用設(shè)計(jì)模式中的Singleton單例模式來開發(fā)iOS應(yīng)用程序

    這篇文章主要介紹了使用設(shè)計(jì)模式中的Singleton單例模式來開發(fā)iOS應(yīng)用程序的例子,示例代碼為傳統(tǒng)的Objective-C語(yǔ)言,需要的朋友可以參考下
    2016-03-03
  • Flutter?RendererBinding作用源碼分析

    Flutter?RendererBinding作用源碼分析

    這篇文章主要為大家介紹了Flutter?RendererBinding作用和內(nèi)部一些重要的類源碼分析,希望此文能給你帶來收獲.有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 最新評(píng)論

    徐水县| 江永县| 老河口市| 兰考县| 巴中市| 昌平区| 达日县| 图片| 德惠市| 西宁市| 昌宁县| 漠河县| 柳江县| 黄龙县| 临泽县| 太白县| 永昌县| 贵南县| 云南省| 正阳县| 奉贤区| 镇沅| 塔城市| 喀喇沁旗| 乌苏市| 灵台县| 荥阳市| 凤凰县| 行唐县| 麻江县| 兴宁市| 阿巴嘎旗| 达拉特旗| 阿城市| 迁安市| 江西省| 平利县| 公主岭市| 九龙坡区| 通江县| 新疆|