国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【iOS】探索ARC的實現(xiàn)

這篇具有很好參考價值的文章主要介紹了【iOS】探索ARC的實現(xiàn)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

ARC在編譯期和運行期做了什么?

ARC (Automatic Reference Counting)是Objective-C在iOS 5.0之后提供的一種自動內(nèi)存管理機(jī)制。它幫助開發(fā)者管理應(yīng)用程序的內(nèi)存使用,減少了因為忘記釋放內(nèi)存導(dǎo)致的內(nèi)存泄漏問題,以及過早釋放內(nèi)存引發(fā)的程序崩潰問題。ARC工作在編譯期和運行期做了以下事情:

編譯期:

  1. 自動插入Retain(引用計數(shù)+1)和Release(引用計數(shù)-1)、Autorelease(延遲引用計數(shù)-1)代碼: 當(dāng)對象被創(chuàng)建或引用傳遞時,引用計數(shù)+1;當(dāng)對象不再使用時,ARC會自動插入釋放內(nèi)存的代碼,從而使引用計數(shù)-1。如果發(fā)現(xiàn)在同一個對象上執(zhí)行了多次“保留”與“釋放”操作,那么ARC有時可以成對的移除這兩個操作。
  2. 檢查代碼,如果發(fā)現(xiàn)明顯的所有權(quán)違規(guī)問題或者循環(huán)引用,編譯器會給出警告。編譯器還會為你生成合適的dealloc方法。
  3. 使用ARC后,編譯器會自動管理autoreleasepool,進(jìn)行合理的創(chuàng)建和釋放使內(nèi)存達(dá)到穩(wěn)定的狀態(tài),無需開發(fā)者手動管理。
  4. ARC更新@property屬性的默認(rèn)語義。像強(qiáng)(strong)弱(weak)引用就是這種情況。強(qiáng)引用會自動增加對象的引用計數(shù),而弱引用則不會。

運行期:

  1. 在運行階段,根據(jù)對象的引用情況,自動調(diào)用release以及autorelease,以減少或延遲引用計數(shù)。引用計數(shù)為0的對象會被立即釋放
  2. 通過對被引用對象的追蹤,ARC能夠自動破解一部分循環(huán)引用,例如:通過引入weak屬性,它不會增加對象的引用計數(shù),這樣一個對象即使被另一個對象通過weak引用,也能夠被正確釋放。
  3. 除釋放對象之外,ARC還負(fù)責(zé)清空所有弱引用(weak reference)的值,阻止野指針的問題發(fā)生。

另外:ARC并不是內(nèi)存管理的終極解決方案,它并不能處理所有情況。比如,如果代碼中存在強(qiáng)循環(huán)引用,即使采用了ARC也無法自動解決。在這種情況下,開發(fā)者需要找出并打破這種循環(huán)。所以,編程者仍然需要理解引用計數(shù)和內(nèi)存管理的基本原理,合理地設(shè)計代碼,避免循環(huán)引用的發(fā)生

block 是如何在 ARC 中工作的?

在ARC下,編譯器會根據(jù)情況自動將棧上的block復(fù)制到堆上,比如block作為函數(shù)返回值時,這樣你就不必再調(diào)用Block Copy。
需要注意的一件事是,在ARC下,NSString * __block myString這樣寫的話,block會對NSString對象強(qiáng)引用,而不是造成懸垂指針問題。如果你要和MRC保持一致,請使用__block NSString * __unsafe_unretained myString或(更好的是)使用__block NSString * __weak myString

ARC的實現(xiàn)分析

__strong

自己生成并持有

	id  __strong obj0 = [[NSObject alloc] init];
    NSLog(@"%@", obj0);

我們轉(zhuǎn)成匯編之后發(fā)現(xiàn)整個的執(zhí)行過程如下:
【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

主要經(jīng)歷的方法如下:

//初始化的兩個方法如下:
objc_alloc_init
objc_storeStrong
//所有程序執(zhí)行完之后:
objc_autoreleasePoolPop

所以我們直接來看storeStrong方法。

storeStrong

在runtime文件中找到這個函數(shù)
如下
objc_storeStrong(id *location, id obj)
{
	//用prev保留被賦值對象原來所指向的對象
    id prev = *location;
    //如果所賦的值和被賦值對象所指的對象是同一個,就直接return不進(jìn)行任何操作
    if (obj == prev) {
        return;
    }
    //如果所賦的值和被賦值對象所指的對象不是同一個
    //就先objc_retain使所賦的值對象的引用計數(shù)+1(因為賦值成功之后要持有)
    objc_retain(obj);
    //改變被賦值對象所指向的對象為新的對象
    *location = obj;
    //因為prev保留了被賦值對象原來所指向的對象,所以對prev進(jìn)行objc_release使原來的舊對象引用計數(shù)-1,因為現(xiàn)在我們的被賦值對象已經(jīng)不指向它了
    objc_release(prev);
}

例子:(賦值操作時)
obj = otherObj;
//會變成如下函數(shù)調(diào)用
objc_storeStrong(&obj, otherObj);

其中做了四件事:

  1. 檢查輸入的 obj 地址 和指針指向的地址是否相同。
  2. 持有對象,引用計數(shù) + 1 。
  3. 指針指向 obj。
  4. 原來指向的對象引用計數(shù) - 1(釋放對象)。

對于這里傳入的NULL來說這就等同于向?qū)ο蟀l(fā)送release消息

詳細(xì)邏輯思路見上方代碼中注釋講解。

SideTable散列表

內(nèi)存管理主要結(jié)構(gòu)代碼

struct SideTable {
    spinlock_t slock; // 保證原子操作的自旋鎖
    RefcountMap refcnts; // 引用計數(shù)的 hash 表
    weak_table_t weak_table; // weak 引用全局 hash 表
};

objc_retain

來學(xué)習(xí)一下objc_object的具體實現(xiàn)

objc_retain(id obj)
{
    if (!obj) return obj;
    if (obj->isTaggedPointer()) return obj;
    return obj->retain();
}

再接著看最后所調(diào)用的retain()方法:

objc_object::retain()
{
    assert(!isTaggedPointer());

    if (fastpath(!ISA()->hasCustomRR())) {
        return rootRetain();
    }

    return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);
}

系統(tǒng)會對是否支持NONPOINTER_ISA進(jìn)行不同的處理
每個OC對象都含有一個isa指針,__arm64__之前,isa僅僅是一個指針,保存著對象或類對象內(nèi)存地址,在__arm64__架構(gòu)之后,apple對isa進(jìn)行了優(yōu)化,變成了一個共用體(union)結(jié)構(gòu),同時使用位域來存儲更多的信息。

union isa_t 
{
    Class cls;
    uintptr_t bits;
    struct {
         uintptr_t nonpointer        : 1;//->表示使用優(yōu)化的isa指針
         uintptr_t has_assoc         : 1;//->是否包含關(guān)聯(lián)對象
         uintptr_t has_cxx_dtor      : 1;//->是否設(shè)置了析構(gòu)函數(shù),如果沒有,釋放對象更快
         uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 ->類的指針
         uintptr_t magic             : 6;//->固定值,用于判斷是否完成初始化
         uintptr_t weakly_referenced : 1;//->對象是否被弱引用
         uintptr_t deallocating      : 1;//->對象是否正在銷毀
         uintptr_t has_sidetable_rc  : 1;//1->在extra_rc存儲引用計數(shù)將要溢出的時候,借助Sidetable(散列表)存儲引用計數(shù),has_sidetable_rc設(shè)置成1,未溢出的時候為0
        uintptr_t extra_rc          : 19;  //->存儲引用計數(shù)
    };
};

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

支持Nonpointer isa的處理
objc_object::rootRetain()
{
    return rootRetain(false, false);
}

ALWAYS_INLINE id 
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
    if (isTaggedPointer()) return (id)this;

    bool sideTableLocked = false;
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    do {
        transcribeToSideTable = false;
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            // 2、SideTable散列表方法
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain();
        }
        if (slowpath(tryRetain && newisa.deallocating)) {
            // 正在釋放
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            return nil;
        }
        uintptr_t carry;
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // 應(yīng)用計數(shù)extra_rc++
        // 如果newisa.extra_rc++ 溢出, carry==1
        if (slowpath(carry)) {
            // 溢出
            if (!handleOverflow) {
                ClearExclusive(&isa.bits); // 空操作(系統(tǒng)預(yù)留)
                return rootRetain_overflow(tryRetain);// 再次調(diào)用rootRetain(tryRetain,YES)
            }
            // 執(zhí)行rootRetain_overflow會來到這里,就把extra_rc對應(yīng)的數(shù)值(一半)存到SideTable
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF; // 溢出了,設(shè)置為一半,保存一半到SideTable
            newisa.has_sidetable_rc = true; // 標(biāo)記借用SideTable存儲
        }
        // StoreExclusive保存newisa.bits到isa.bits,保存成功返回YES
        // 這里while判斷一次就結(jié)束了
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));

    if (slowpath(transcribeToSideTable)) {
        // 借位保存:把RC_HALF(一半)存入SideTable
        sidetable_addExtraRC_nolock(RC_HALF);
    }

    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    return (id)this;
}


不支持的處理

objc_object::rootRetain()
{
    if (isTaggedPointer()) return (id)this;
    return sidetable_retain();
}

id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    SideTable& table = SideTables()[this];
    
    table.lock();
    size_t& refcntStorage = table.refcnts[this];
    // 判斷是否溢出,溢出了就是引用計數(shù)太大了,不管理了
    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
        // 引用計數(shù)加1,(上圖可知,偏移2位,SIDE_TABLE_RC_ONE = 1<<2)
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    table.unlock();
    return (id)this;
}

可以看到不支持Nonpointer isa的處理就是直接sidetable_retain,這是由于計數(shù)都存儲在sidetable中了,處理邏輯較支持Nonpointer isa的情況要簡單一些。

不支持Nonpointer isa 的處理
去sidetable取出計數(shù)信息 執(zhí)行加一操作

支持Nonpointer isa的處理

  • 首先判斷是否為標(biāo)簽指針類型 如果是 直接返回
  • 進(jìn)入do-while處理邏輯
  1. 先判斷是否為 其一定支持Nonpointer isa的架構(gòu),但是isa沒有額外信息
    如果沒有額外信息 那就和不支持意義一樣(判斷是否有優(yōu)化) 引用計數(shù)存儲在sidetable中,走sidetable的引用計數(shù)+1的流程
  2. 判斷對象是否正在釋放,如果正在釋放則執(zhí)行dealloc流程。
  3. 有存儲額外信息,包含引用計數(shù)。我們嘗試對isa中的extra_rc++加一進(jìn)行測試
    3.1 如果沒有溢出越界的情況,我們將isa的值修改為extra_rc++之后的值
    3.2 如果有溢出 將一半的計數(shù)存儲到extra_rc,另一半存儲到sidetable中去 設(shè)置設(shè)置標(biāo)志位位true
    【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

retain的總結(jié):

如果isa可以存儲額外信息,那么有extra_rc位用來存儲引用計數(shù),當(dāng)引用計數(shù)滿了之后 就會存儲到sidetable中 。

retain的流程也是針對isa是否支持存儲信息分別進(jìn)行處理

當(dāng)extra_rc存儲溢出了,這個時候是一半(extra_rc能表示的最大值+1的一半)在extra_rc一半存儲在sidetable中,這里跟release的操作是對應(yīng)的(extra_rc不夠減了也是去sidetableextra_rc最大值的一半的計數(shù)),這樣設(shè)計的好處避免了頻繁的去sidetable中讀取計數(shù)信息—假如我們溢出了把計數(shù)全部存到sidetable中去,那么有release的時候,extra_rc也不夠減了,又去借,這就大大降低了效率,比起直接操作isa。

這個優(yōu)化的好處就是我們省去了頻繁去sidetable中讀取計數(shù)信息,從而大大提高了效率,這樣的話因為平時絕大多處操作都是普通的retainrelease,所以都可以得到優(yōu)化,而我們?nèi)绻x取引用計數(shù)的值的話就相對麻煩一點,需要sidetableextra_rc兩者相加,但是讀取引用計數(shù)值的方法使用率幾乎為零,也就是調(diào)試的時候會用到而已。

objc_release

來學(xué)習(xí)一下objc_release的具體實現(xiàn)

objc_release(id obj)
{
    if (obj->isTaggedPointerOrNil()) return;
    return obj->release();
}

下面我們看一下真正的release方法:

// handleUnderflow 參數(shù)看似是一個 bool 類型的表示是否處理下溢出,
// 當(dāng)溢出發(fā)生了的話是必須要處理的,如果 handleUnderflow 為 false,
// 那么它會借一個 rootRelease_underflow 函數(shù),并再次調(diào)用 rootRelease 函數(shù),
// 并把 handleUnderflow 參數(shù)傳遞 true。

ALWAYS_INLINE bool 
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{   
    // 如果是 Tagged Pointer 直接返回 false,Tagged Pointer 不參與引用計數(shù)處理,它內(nèi)存位于棧區(qū),由系統(tǒng)處理
    if (isTaggedPointer()) return false;

    // 標(biāo)記 SideTable 是否加鎖了
    bool sideTableLocked = false;

    // 臨時變量存放舊的 isa
    isa_t oldisa;
    // 臨時變量存放字段修改后的 isa
    isa_t newisa;

 retry:
    do {
        // 以原子方式讀到 &isa.bits 的數(shù)據(jù)
        oldisa = LoadExclusive(&isa.bits);
        // 把 oldisa 賦值給 newisa,此時 isa.bits/oldisa/newisa 三者是相同的
        newisa = oldisa;
        
        if (slowpath(!newisa.nonpointer)) {
            // 如果對象的 isa 只是原始指針 (Class isa/Class cls)
            
            // __arm64__ && !__arm642__ 平臺下,取消 &isa.bits 的獨占訪問標(biāo)記
            // x86_64 下什么都不需要做,對它而言上面的 LoadExclusive 也只是一個原子讀取 (atomic_load)
            ClearExclusive(&isa.bits);
            
            // 如果當(dāng)前對象是元類對象,則直接返回 false 
            if (rawISA()->isMetaClass()) return false;
            
            // 如果當(dāng)前 SideTable 加鎖了則進(jìn)行解鎖
            if (sideTableLocked) sidetable_unlock();
            
            // 只針對 isa 是原始 Class cls 的對象調(diào)用的 sidetable_release 函數(shù)
            return sidetable_release(performDealloc);
        }
        
        // don't check newisa.fast_rr; we already called any RR overrides
        // 不要檢查 newisa.fast_rr; 我們之前已經(jīng)調(diào)用過所有 RR 的重載
        
        // extra_rc-- 
        uintptr_t carry;
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        
        // 如果發(fā)生了下溢出的話,要進(jìn)行處理,如果沒有發(fā)生的話就是結(jié)束循環(huán),解鎖并執(zhí)行 return false;
        if (slowpath(carry)) {
            // don't ClearExclusive()
            // 不執(zhí)行 ClearExclusive()
            // 這里直接 goto 到 underflow 中去處理溢出
            goto underflow;
        }
        
    // 這里結(jié)束循環(huán)的方式同 rootRetain 函數(shù),都是為了保證 isa.bits 能正確修改
    // StoreExclusive 和 StoreReleaseExclusive 的區(qū)別在于 memory_order_relaxed 和 memory_order_release
    // 可參考 https://en.cppreference.com/w/cpp/atomic/memory_order
    
    // 當(dāng) &isa.bits 與 oldisa.bits 相同時,把 newisa.bits 復(fù)制給 &isa.bits,并返回 true
    // 當(dāng) &isa.bits 與 oldisa.bits 不同時,
    // 把 oldisa.bits 復(fù)制給 &isa.bits, 并返回 false (此時會繼續(xù)進(jìn)行 do wehile 循環(huán))
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits)));
    
    // 如果未下溢出的話,不需要 goto underflow,如果 Sidetable 加鎖了,
    // 則進(jìn)行解鎖,然后返回 false,函數(shù)執(zhí)行結(jié)束
    if (slowpath(sideTableLocked)) sidetable_unlock();
    return false;

 underflow:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate
    // newisa.extra_rc-- 發(fā)生溢出時,有兩種方式進(jìn)行處理:
    // 1. 如果 SideTable 中有保存對象的引用計數(shù)的話可以從 SideTable 中借用
    // 2. 如果 SideTable 中沒有保存對象的引用計數(shù)的話,表示對象需要執(zhí)行銷毀了

    // abandon newisa to undo the decrement
    newisa = oldisa;

    if (slowpath(newisa.has_sidetable_rc)) {
        // 如果 newisa.has_sidetable_rc 為 true,表示在 SideTable 中有保存對象的引用計數(shù)
        if (!handleUnderflow) {
            ClearExclusive(&isa.bits);
            
            // 如果 handleUnderflow 為 false,則調(diào)用 rootRelease_underflow,“遞歸” 調(diào)用 rootRelease 處理溢出
            return rootRelease_underflow(performDealloc);
        }

        // Transfer retain count from side table to inline storage.
        // 將 retain count 從 SideTable 中轉(zhuǎn)移到 isa.extra_rc 中保存。

        if (!sideTableLocked) {
            // 如果 SideTable 未加鎖
            
            // 同上,清除獨占標(biāo)記
            ClearExclusive(&isa.bits);
            
            // 給 SideTable 加鎖
            sidetable_lock();
            // 并把加鎖標(biāo)記置為 true
            sideTableLocked = true;
            
            // Need to start over to avoid a race against the nonpointer -> raw pointer transition.
            
            // 回到 retry
            goto retry;
        }

        // Try to remove some retain counts from the side table.
        // 嘗試從 SideTable 中移除一些引用計數(shù)。
        
        // 是從 SideTable 借一些引用計數(shù)出來,borrowed 是借到的值,可能是 0,也可能是 RC_HALF
        // refcnts 中保存的引用計數(shù)是 RC_HALF 的整數(shù)倍,
        // 每次 retain 溢出時都是往 refcnts 中轉(zhuǎn)移 RC_HALF,
        // 剩下的 RC_HALF 放在 extra_rc 字段中
        size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

        // To avoid races, has_sidetable_rc must remain set even if the side table count is now zero.
        // 為了避免競態(tài),即使 SideTable 計數(shù)現(xiàn)在為零,也必須保持 has_sidetable_rc 之前的設(shè)置。
        
        if (borrowed > 0) {
            // borrowed 表示從 SideTable 借到引用計數(shù)了
            
            // Side table retain count decreased.
            // SideTable 引用計數(shù) 減少。
            // Try to add them to the inline count.
            // 嘗試將借來的引用計數(shù)增加到 extra_rc 中。
            
            // 賦值。(包含減 1 的操作)
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too
            
            // 原子保存修改后的 isa.bits
            bool stored = StoreReleaseExclusive(&isa.bits, 
                                                oldisa.bits, newisa.bits);
            if (!stored) {
                // 如果失敗的話
                
                // Inline update failed. 
                // extra_rc 更新失敗。
                
                // Try it again right now. 
                // This prevents livelock on LL/SC architectures where the side
                // table access itself may have dropped the reservation.
                // 立即進(jìn)行重試。
                // 這樣可以防止在 LL/SC體系結(jié)構(gòu)上發(fā)生 livelock,在這種情況下 SideTable 訪問本身可能已取消預(yù)留。
                // 活鎖可參考: https://www.zhihu.com/question/20566246
                
                isa_t oldisa2 = LoadExclusive(&isa.bits);
                isa_t newisa2 = oldisa2;
                
                if (newisa2.nonpointer) {
                    uintptr_t overflow;
                    // 把借來的引用計數(shù)增加到 extra_rc 中
                    newisa2.bits = 
                        addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                    if (!overflow) {
                        // 如果還是失敗的話,下面 goto retry 再重來
                        stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                       newisa2.bits);
                    }
                }
            }

            if (!stored) {
                // 如果還是失敗了。
                // Inline update failed.
                // Put the retains back in the side table.
                // 把從 SideTable 借來的引用計數(shù)還放回到 SideTable 中去。
                
                sidetable_addExtraRC_nolock(borrowed);
                
                // 然后直接 goto retry; 進(jìn)行全盤重試
                goto retry;
            }

            // Decrement successful after borrowing from side table.
            // 減去從 SideTable 借來的引用計數(shù)成功。
            
            // This decrement cannot be the deallocating decrement
            // - the side table lock and has_sidetable_rc bit
            // ensure that if everyone else tried to -release while we worked, 
            // the last one would block.
            
            // 解鎖
            sidetable_unlock();
            // 返回 false 
            return false;
        }
        else {
            // SideTable 是空的,執(zhí)行 dealloc 分支
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

    // Really deallocate.
    // 執(zhí)行銷毀。

    if (slowpath(newisa.deallocating)) {
        // 如果對象已經(jīng)被標(biāo)記了正在執(zhí)行釋放...
        // 這里又進(jìn)行釋放,明顯是發(fā)生了過度釋放...
        
        // 清除獨占標(biāo)記
        ClearExclusive(&isa.bits);
        
        // 如果 SideTable 加鎖了則進(jìn)行解鎖
        if (sideTableLocked) sidetable_unlock();
        // 調(diào)用 overrelease_error,crash 報錯...
        // 對象在銷毀的過程中過度釋放;中斷 objc_overrelease_during_dealloc_error 進(jìn)行調(diào)試
        return overrelease_error();
        // does not actually return
    }
    
    // 把對象的 isa 的 deallocating 置為 true。isa 的又一個字段被設(shè)置了,越來的越多的字段被發(fā)現(xiàn)設(shè)置位置了。 
    newisa.deallocating = true;
    
    // 設(shè)置 &isa.bits,如果失敗,則 goto retry;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

    // 如果加鎖了,則進(jìn)行解鎖。
    if (slowpath(sideTableLocked)) sidetable_unlock();

    // 這個函數(shù)以當(dāng)前的水平實在是看不懂呀...
    __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);

    if (performDealloc) {
        // 如果 performDealloc 為 true,則以消息發(fā)送的方式調(diào)用 dealloc 
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    
    return true;
}
}

sidetable_release

// return uintptr_t instead of bool so that the various raw-isa -release paths all return zero in eax
// 返回 uintptr_t 而不是 bool,以便各種 raw-isa -release路徑在 eax 中都返回零

uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
// 如果當(dāng)前平臺支持 isa 優(yōu)化
#if SUPPORT_NONPOINTER_ISA
    // 如果 isa 是優(yōu)化的 isa 則直接執(zhí)行斷言,
    // sidetable_release 函數(shù)只能在對象的 isa 是原始 isa 時調(diào)用(Class cls)
    ASSERT(!isa.nonpointer);
#endif
    
    // 從全局的 SideTalbes 中找到 this 所處的 SideTable
    SideTable& table = SideTables()[this];
    
    // 臨時變量,標(biāo)記是否需要執(zhí)行 dealloc
    bool do_dealloc = false;
    
    // 加鎖
    table.lock();
    
    // it 的類型是: std::pair<DenseMapIterator<std::pair<Disguised<objc_object>, size_t>>, bool>
    // try_emplace 處理兩種情況:
    // 1. 如果 this 在 refcnts 中還不存在,則給 this 在 buckets 中找一個 BucketT,
    //    KeyT 放 this, ValueT 放 SIDE_TABLE_DEALLOCATING,然后使用這個 BucketT 構(gòu)建一個 iterator,
    //    然后用這個 iterator 和 true 構(gòu)造一個 std::pair<iterator, true> 返回。
    // 2. 如果 this 在 refcnts 中已經(jīng)存在了,則用 this 對應(yīng)的 BucketT 構(gòu)建一個 iterator,
    //    然后用這個 iterator 和 false 構(gòu)造一個 std::pair<iterator, false> 返回。
    auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
    
    // refcnt 是引用計數(shù)值的引用。
    // it.first 是 DenseMapIterator,它的操作符 -> 被重寫了返回的是 DenseMpaIterator 的 Ptr 成員變量,
    // 然后 Ptr 的類型是 BucketT 指針,
    // 然后這里的 ->second 其實就是 BucketT->second,其實就是 size_t,正是保存的對象的引用計數(shù)數(shù)據(jù)。
    auto &refcnt = it.first->second;
    
    if (it.second) {
        // 如果 it.second 為 true,表示 this 第一次放進(jìn) refcnts 中,且 BucketT.second 已經(jīng)被置為 SIDE_TABLE_DEALLOCATING,
        // 標(biāo)記為需要執(zhí)行 dealloc
        do_dealloc = true;
    } else if (refcnt < SIDE_TABLE_DEALLOCATING) {
        // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
        // 如果 refcnt < SIDE_TABLE_DEALLOCATING,那可能的情況就是 SIDE_TABLE_WEAKLY_REFERENCED 或者為 0
        // 標(biāo)記為需要執(zhí)行 dealloc
        do_dealloc = true;
        
        // 與 SIDE_TABLE_DEALLOCATING 執(zhí)行或操作,表示把 refcnt 標(biāo)記為 DEALLOCATING
        refcnt |= SIDE_TABLE_DEALLOCATING;
    } else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
        // refcnt & SIDE_TABLE_RC_PINNED 值為 false 的話表示,
        // rcfcnts 中保存的 this 對應(yīng)的 BucketT 的 size_t 還沒有溢出,還可正常進(jìn)行操作存儲 this 的引用計數(shù)
        // refcnt 減去 SIDE_TABLE_RC_ONE
        refcnt -= SIDE_TABLE_RC_ONE;
    }
    
    // 解鎖
    table.unlock();
    
    if (do_dealloc  &&  performDealloc) {
        // 如果 do_dealloc 被標(biāo)記為需要 dealloc 并且入?yún)?performDealloc 為 true,
        // 則以 objc_msgSend 消息發(fā)送的方式調(diào)用對象的 dealloc 方法
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    
    return do_dealloc;
}

在上面的代碼當(dāng)中有兩個宏定義:

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

現(xiàn)在release也很好理解了:

  1. 依舊是判斷是否為taggedPointer,如果是,直接返回false,不需要dealloc
  2. 判斷是否有優(yōu)化 如果沒有 就直接操作散列表,使引用計數(shù)-1
  3. 判斷是引用計數(shù)為否為0 如果是0則執(zhí)行dealloc流程
  4. isa有優(yōu)化,則對象的isa位存儲的引用計數(shù)減一,且通過carry判斷是否向下溢出了 結(jié)果為負(fù)數(shù)(下圖有點問題 應(yīng)該是判斷是有向下溢出),如果是,如果到-1 就放棄newisa改為old,并將散列表中一半引用計數(shù)取出來,然后將這一半引用計數(shù)減一在存到isaextra_rc
  5. 如果sidetable的引用計數(shù)為0,對象進(jìn)行dealloc流程

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

其實和retain一樣 不過release操作變成-1 并且需要注意從sidetable中的一半減一放入

retainCount

如果對象的isa是非指針的話,引用計數(shù)同時在 extra_rc 字段和 SideTable 中保存,要求它們的和。如果對象的isa是原始isa的話,對象的引用計數(shù)數(shù)據(jù)只保存在 SideTable 中。

  1. 當(dāng)對象的isa經(jīng)過優(yōu)化,首先獲取isa位域extra_rc中的引用計數(shù),默認(rèn)會+1(防止你沒持有就要打?。?,uintptr_t rc = 1 + bits.extra_rc;然后獲取散列表的引用計數(shù)表中的引用計數(shù),兩者相加得到對象的最終的引用計數(shù)
  2. 當(dāng)對象的isa沒有經(jīng)過優(yōu)化,則直接獲取散列表的引用計數(shù)表中的引用計數(shù),返回。
  3. 當(dāng)我們alloc一個對象時,然后調(diào)用retainCount函數(shù),得到對象的引用計數(shù)為1。這是因為在底層rootRetainCount方法中,引用計數(shù)默認(rèn)+1了,這里只有對引用計數(shù)的讀取操作,是沒有寫入操作的,簡單來說就是:為了防止alloc創(chuàng)建的對象被釋放(引用計數(shù)為0會被釋放),所以在編譯階段,程序底層默認(rèn)進(jìn)行了+1操作。實際上在extra_rc中的引用計數(shù)仍然為0(因為extra_rc中存放的引用計數(shù)值是除該對象本身之外的引用計數(shù)數(shù)量)

所以 通過alloc或者new這樣賦值來新建一個對象 ARC MRC環(huán)境下都是1 這個1是底層默認(rèn)的返回值加一 沒有調(diào)用retain 其他強(qiáng)引用 才會調(diào)用objc_retain來持有

runtime源碼中找到retainCount供大家參考:

inline uintptr_t 
objc_object::rootRetainCount()
{
    // 如果是 Tagged Pointer 的話,獲取它的引用計數(shù)則直接返回 (uintptr_t)this
    if (isTaggedPointer()) return (uintptr_t)this;
    
    // 加鎖
    sidetable_lock();
    
    // 以原子方式加載 &isa.bits 數(shù)據(jù)
    isa_t bits = LoadExclusive(&isa.bits);
    // 如果是 __arm64__ && !__arm64e__ 平臺下,要清除獨占標(biāo)記
    ClearExclusive(&isa.bits);
    
    if (bits.nonpointer) {
        // 如果對象的 isa 是非指針的話,引用計數(shù)同時在 extra_rc 字段和 SideTable 中保存,要求它們的和
        // 這里加 1, 是因為 extra_rc 存儲的是對象本身之外的引用計數(shù)的數(shù)量(這個加1操作也就是為什么我們新alloc等初始化一個對象之后,打印它的引用計數(shù)值為1)
        uintptr_t rc = 1 + bits.extra_rc;
        
        // 如果 has_sidetable_rc 位為 1,則表示在 SideTable 中也保存有對象的引用計數(shù)數(shù)據(jù)
        if (bits.has_sidetable_rc) {
            // 找到對象的在 SideTable 中的引用計數(shù)并增加到 rc 中
            rc += sidetable_getExtraRC_nolock();
        }
        // 解鎖
        sidetable_unlock();
        // 返回 rc
        return rc;
    }

    sidetable_unlock();
    // 如果對象的 isa 是原始 isa 的話,對象的引用計數(shù)數(shù)據(jù)只保存在 SideTable 中
    return sidetable_retainCount();
}

isa如果優(yōu)化過,即支持Nonpointer isa,則在sidetable中查找引用計數(shù)的函數(shù)如下:

size_t 
objc_object::sidetable_getExtraRC_nolock()
{
    // 此函數(shù)只限定 isa 是非指針的對象調(diào)用
    ASSERT(isa.nonpointer);
    
    // 從全局的 SideTables 中找到 this 所處的 SideTable
    SideTable& table = SideTables()[this];
    // 查找對象的引用計數(shù)
    RefcountMap::iterator it = table.refcnts.find(this);
    // 如果未找到,返回 0
    if (it == table.refcnts.end()) return 0;
    // 如果找到了做一次右移操作,后兩位是預(yù)留的標(biāo)記位
    else return it->second >> SIDE_TABLE_RC_SHIFT;
}

不支持Nonpointer isa的話,在sidetable中查找引用計數(shù)的函數(shù)如下:

uintptr_t
objc_object::sidetable_retainCount()
{
    // 找到 this 所在的 SideTable
    SideTable& table = SideTables()[this];

    // refcnt_result 初始為 1,因為 SideTable 中存儲的是對象本身之外的引用計數(shù)的數(shù)量
    size_t refcnt_result = 1;
    
    // 加鎖
    table.lock();
    
    // 在 refcnts 中查找對象的引用計數(shù)
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        // this is valid for SIDE_TABLE_RC_PINNED too
        // 這也對 SIDE_TABLE_RC_PINNED 有效
        
        // 移位并增加到 refcnt_result
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
    }
    
    // 解鎖
    table.unlock();
    return refcnt_result;
}

retainCount相關(guān)流程圖如下:

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

在學(xué)完了releaseretain之后,我們淺淺地總結(jié)一下: 在我們alloc初始化完一個對象的過程中,系統(tǒng)在編譯階段,程序底層默認(rèn)對對象進(jìn)行了引用計數(shù)+1操作,但是這個1不會出現(xiàn)在sidetable中,也不會出現(xiàn)在extra_rc中,因為sidetableextra_rc當(dāng)中存放的都是該對象本身之外的引用計數(shù)的數(shù)量,所以初始狀態(tài)sidetableextra_rc中的值都是0,然后我們后續(xù)進(jìn)行的retainrelease操作都是針對sidetableextra_rc中的引用計數(shù)進(jìn)行+1或-1。

非自己生成并持有

	id __strong obj = [NSMutableArray array];
    NSLog(@"%@", obj);

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

我們發(fā)現(xiàn)出現(xiàn)了objc_retainAutoreleasedReturnValue這個方法

接著我們就探究其原理:

先看一個例子:

	@autoreleasepool {
       __autoreleasing NSObject *obj = [NSObject new];
    }
	該代碼對應(yīng)的偽代碼是:
	// 獲取哨兵POOL_SENTINEL
    void * atautoreleasepoolobj = objc_autoreleasePoolPush();
    {
        __autoreleasing NSObject *obj = [NSObject new];
    }
    // 就是release哨兵之后的autorelease對象。
    objc_autoreleasePoolPop(atautoreleasepoolobj);

autorelease調(diào)用棧如下:

- [NSObject autorelease]
└── id objc_object::rootAutorelease()
    └─ id objc_object::rootAutorelease2()
       └─ static id AutoreleasePoolPage::autorelease(id obj)
          └─ static id AutoreleasePoolPage::autoreleaseFast(id obj)
             ├─ id *add(id obj)
             ├─ static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
             │  ├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)
             │  └─ id *add(id obj)
             └─ static id *autoreleaseNoPage(id obj)
                ├─ AutoreleasePoolPage(AutoreleasePoolPage *newParent)
                └─ id *add(id obj)

一個autorelease對象在什么時刻釋放?
答案是:

  1. 手動指定Autoreleasepool:當(dāng)前Autoreleasepool作用域大括號結(jié)束時釋放;
  2. 不手動指定:autorelease對象會被添加到最近一次創(chuàng)建的autoreleasepool中,并在當(dāng)前的runloop迭代結(jié)束時候釋放。

例如: 主RunloopAutoreleasepool管理的流程:
Runloop中,檢測到觸摸事件,創(chuàng)建事件,創(chuàng)建Autoreleasepoolautorelease對象加入pool中,事件完成,Runloop運行循環(huán)將要結(jié)束,釋放Autoreleasepool,向pool中對象發(fā)送release消息,Runloop休眠。

autorelease 進(jìn)行的非持有方法的優(yōu)化(自動添加到自動釋放池):

  1. alloc/new/copy/mutableCopy—持有對象方法會自動添加到自動釋放池
  2. 其他類方法返回的對象,如下面的createObject就會自動添加到自動釋放池
@implementation ObjectTest
+ (instancetype)createObject {
    return [self new];
}

接著我們來看這兩個方法本尊:

id objc_autoreleaseReturnValue(id obj)
{
    // prepareOptimizedReturn判斷是否可以TSL優(yōu)化,可以則標(biāo)記,YES--就不需要調(diào)用 objc_autorelease(),優(yōu)化性能
    if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;

    return objc_autorelease(obj);
}
id objc_retainAutoreleasedReturnValue(id obj)
{
  // 如果之前 objc_autoreleaseReturnValue() 存入的標(biāo)志位為 ReturnAtPlus1,則直接返回對象,無需調(diào)用 objc_retain(),優(yōu)化性能
    if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
    return objc_retain(obj);
}

然后是這兩個方法中if判斷的條件調(diào)用的函數(shù):



static ALWAYS_INLINE bool 
prepareOptimizedReturn(ReturnDisposition disposition)
{
	//獲取返回標(biāo)記
    assert(getReturnDisposition() == ReturnAtPlus0);

    if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
        if (disposition) setReturnDisposition(disposition);
        return true;
    }

    return false;
}

static ALWAYS_INLINE ReturnDisposition 
acceptOptimizedReturn()
{
    ReturnDisposition disposition = getReturnDisposition();
    setReturnDisposition(ReturnAtPlus0);  // reset to the unoptimized state
    return disposition;
}

TLS 全稱為 Thread Local Storage,是每個線程專有的鍵值存儲:

/*在某個線程上的函數(shù)調(diào)用棧上相鄰兩個函數(shù)對 TLS 進(jìn)行了存取,這中間肯定不會有別的程序『插手』。
所以 getReturnDisposition() 和 setReturnDisposition() 的實現(xiàn)比較簡單,不需要判斷考慮是針對哪個對象的 Disposition 進(jìn)行存取,因為當(dāng)前線程上下文中只處理唯一的對象,保證不會亂掉。 */

static ALWAYS_INLINE void 
setReturnDisposition(ReturnDisposition disposition)
{
    tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}

callerAcceptsOptimizedReturn(__builtin_return_address(0))函數(shù)在不同架構(gòu)的 CPU 上實現(xiàn)也是不一樣的。 主要作用:

__builtin_return_address(0)獲取當(dāng)前函數(shù)返回地址,傳入 callerAcceptsOptimizedReturn 判斷調(diào)用方是否緊接著調(diào)用了 objc_retainAutoreleasedReturnValue
當(dāng)判斷調(diào)用方緊接著調(diào)用了 objc_retainAutoreleasedReturnValue 或者 objc_unsafeClaimAutoreleasedReturnValue
直接返回當(dāng)前對象地址,而不執(zhí)行retain與autorelease操作.

其作用就是得到函數(shù)的返回地址,0–表示返回當(dāng)前函數(shù)的返回地址,1–表示返回當(dāng)前函數(shù)的調(diào)用方的返回地址;

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

ARC 會視情況在調(diào)用方法時可能會添加 retain ,在方法內(nèi)部返回時可能會添加 autorelease ,經(jīng)過優(yōu)化后很可能會抵消。

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

1、持有、無引用:

- (void)test {
    [BBObject new];
}

編譯器編譯后的偽代碼:

- (void)test {
    objc_release([BBObject new]) ;
}

2、持有、局部變量引用 __strong:

- (void)test {
    __strong BBObject * obj = [BBObject new];
}

編譯器編譯后的偽代碼:

- (void)test {
    id temp = [BBObject new];
    objc_storeStrong(&temp,nil);//相當(dāng)于tmp指向?qū)ο髨?zhí)行release
}

3、持有、外部變量引用:

- (void)test {
    self.obj = [BBObject new];
}

編譯器編譯后的偽代碼:

- (void)test{
    id temp = [BBObject new];
    [self setObj:temp];//setter方法執(zhí)行objc_storeStrong
    objc_release(temp);
}
- (void)setObj:(id aObj) {
    objc_storeStrong(&_obj, aObj);
}

4、不持有、無引用:

- (void)test {
    [BBObject createObj];
}

編譯器編譯后的偽代碼:

+ (instancetype) createObj {
    id tmp = [self new];
    return objc_autoreleaseReturnValue(tmp); // 系統(tǒng)可能會調(diào)用[tmp autorelease] 
}
- (void)test { 
    objc_unsafeClaimAutoreleasedReturnValue([BBObject createObj]); 
}

5、不持有、局部變量引用:

- (void)test {
    BBObject * obj1 = [BBObject createObj];
}

編譯器編譯后的偽代碼:

+ (instancetype) createObj {
    id tmp = [self new];
    return objc_autoreleaseReturnValue(tmp); // 系統(tǒng)可能會調(diào)用[tmp autorelease] 
}
- (void)test {
    id obj1 = objc_retainAutoreleasedReturnValue([BBObject createObj]);  
    objc_storeStrong(& obj1,nil); 
}

發(fā)現(xiàn)obj1指向的對象不會加入autoreleasepool

6、不持有、外部變量引用:

+ (instancetype) createObj {
    id tmp = [self new];
    return objc_autoreleaseReturnValue(tmp); // 系統(tǒng)可能會調(diào)用[tmp autorelease] 
}
- (void)test {
    self.obj = [BBObject createObj];
}

編譯后的偽代碼:

- (void)test {
    id temp = _objc_retainAutoreleasedReturnValue([Foo createFoo]); 
    [self setObj:temp]; // setter方法執(zhí)行objc_storeStrong
    objc_release(temp);
}

總結(jié)非自己生成并持有

  • objc_autoreleasedReturnValue會檢驗調(diào)用者是否會對該對象執(zhí)行retain操作,如果會的話就不執(zhí)行autorelease,直接設(shè)置標(biāo)志符ReturnAtPlus1
  • objc_retainAutoreleaseReturnValue在檢驗到標(biāo)志符后,也不retain了(后面retain操作),直接返回對象本身,同樣,如果檢測到標(biāo)識符顯示后面沒有retain操作,那么就走一遍retain使其引用計數(shù)加1

所以array這樣的賦值新建一個對象,ARC環(huán)境下引用計數(shù)的1是底層默認(rèn)的返回值加一 沒有調(diào)用retain 其他強(qiáng)引用,才會調(diào)用objc_retain來使引用計數(shù)加一。objc_retainretainAutoreleaseReturnValue調(diào)用的。

一個問題:為什么要傳入(NSError **)這種類型的參數(shù)

這是一個二級指針(指向指針的指針),將一個基本類型的變量通過函數(shù)參數(shù)傳入函數(shù)內(nèi),在函數(shù)內(nèi)如何改變都不會影響到外部變量的值,那如果我們要在函數(shù)內(nèi)部改變外部變量的值,就應(yīng)該將指針的值傳入函數(shù),然后函數(shù)中根據(jù)指針去找到指向的內(nèi)存進(jìn)行修改。

如果函數(shù)參數(shù)本身是一個對象,我們傳入一個對象,對象本身就是一個地址(但是一個一級指針)。

如以下例子:

#import <Foundation/Foundation.h>
#import "StrongTest.h"

void test(StrongTest *obj) {
    obj.name = @"3G Group";
    //重新初始化obj,也就是改變參數(shù)obj的值(因為劃分新的內(nèi)存,對象的地址會變,而obj就是對象在內(nèi)存中的地址)
    obj = [[StrongTest alloc] init];
    obj.name = @"iOS Club";
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        StrongTest *obj = [[StrongTest alloc] init];
        obj.name = @"Xi You";
        
        test(obj);
        NSLog(@"obj.name = %@", obj.name);
    }
    return 0;
}

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

可以看到我們最后的打印結(jié)果中,并沒有打印新初始化的對象的字符串。

這是因為我們使用alloc init后系統(tǒng)會在內(nèi)存中新開辟一塊存儲空間存儲一個新的對象,然后將函數(shù)中的obj存儲的指針值改為這個新的內(nèi)存地址,而函數(shù)外的obj并沒有發(fā)生改變,還是指向原來的這個對象的地址

如果想在函數(shù)中改變函數(shù)外的對象,就需要用到二級指針,即指向指針的指針。

例子如下:

#import <Foundation/Foundation.h>
#import "StrongTest.h"

void test(StrongTest **obj) {
    (*obj).name = @"3G Group";
    
    *obj = [[StrongTest alloc] init];
    (*obj).name = @"iOS Club";
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        StrongTest *obj = [[StrongTest alloc] init];
        obj.name = @"Xi You";
        
        test(&obj);
        NSLog(@"obj.name = %@", obj.name);
    }
    return 0;
}

【iOS】探索ARC的實現(xiàn),iOS源碼學(xué)習(xí),ios,cocoa,macos

可以看到打印的結(jié)果就是我們新初始化后的對象中的字符串。

所以,所以對于NSError **,我們可以在外面新建一個NSError,當(dāng)函數(shù)有錯誤時,新建一個NSError對象并存儲到我們新建的這個NSError對象中。我們就可以通過判斷NSError是否為nil來看函數(shù)運行是否出錯。文章來源地址http://www.zghlxwxcb.cn/news/detail-576480.html

到了這里,關(guān)于【iOS】探索ARC的實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • [學(xué)習(xí)分享]----sylar服務(wù)器框架源碼閱讀--IO協(xié)程調(diào)度模塊

    sylar作者在本站的地址為 這里,也可以查看 作者主頁,也有視頻教程可以 點擊這里。此外,也可以看一下趙路強(qiáng)大佬的關(guān)于sylar協(xié)程模塊的博客 點擊這里,我本人在閱讀sylar源碼的時候也是參考了趙路強(qiáng)大佬的解析 可以點擊這里。 各位看官也可以加我的qq和我討論2511010742

    2024年01月17日
    瀏覽(23)
  • macos編譯libtiff庫給IOS用

    macos編譯libtiff庫給IOS用

    ? ? ?

    2024年02月12日
    瀏覽(19)
  • 短視頻程序源碼,實現(xiàn)ios系統(tǒng)的短視頻緩存

    短視頻程序源碼,實現(xiàn)ios系統(tǒng)的短視頻緩存

    短視頻程序源碼實現(xiàn)視頻播放,對于ios開發(fā)來說其實并不是一個難事兒,簡單幾行代碼就能實現(xiàn),確實,最初的短視頻播放也是基于此,給定視頻url直接丟給系統(tǒng)播放器(AVPlayer)就可以播放了。但是隨著短視頻程序源碼業(yè)務(wù)發(fā)力,短視頻模塊在APP業(yè)務(wù)中承擔(dān)了更多更重要的

    2024年02月08日
    瀏覽(18)
  • Charles證書過期解決方法macos/ios

    Charles證書過期解決方法macos/ios

    今天心血來潮打開Charles想試試看抓包手機(jī)APP(ios),結(jié)果發(fā)現(xiàn)各種x和提示ssl錯誤。開始以為是和魔法的代理沖突或者ip變了,捯飭很久后發(fā)現(xiàn)web的也報錯。 然后搜了一會原因發(fā)現(xiàn)時證書過期了 1、搜索“鑰匙串訪問”,直接搜索“charles”,找到打叉的名稱,直接刪掉 2、打開

    2024年02月03日
    瀏覽(23)
  • MacOS 14 系統(tǒng) XCode15、 Flutter 開發(fā) IOS

    MacOS 14 系統(tǒng) XCode15、 Flutter 開發(fā) IOS

    MacOS14 Sonoma 安裝 Flutter 開發(fā)環(huán)境 MacOS 系統(tǒng) Flutter開發(fā)Android 環(huán)境配置 MacOS 系統(tǒng) Flutter開發(fā)IOS 環(huán)境配置??????? 前面我們已經(jīng)在MacOS14 M3芯片上安裝好 Flutter環(huán)境,包括開發(fā)工具 VsCode 、Android Stuiod,那么flutter如何開發(fā)IOS呢? 我們知道IOS開發(fā)語言為 objcet-c或者 swift,F(xiàn)lutter是

    2024年02月03日
    瀏覽(23)
  • 【iOS】對象的本質(zhì)探索

    【iOS】對象的本質(zhì)探索

    生成C++文件的一些命令: clang -rewrite-objc main.m -o main.cpp:(無法區(qū)分平臺 不建議使用) xcrun -sdk iphonesimulator clang -rewrite-objc main.m -o main.cpp:(模擬器) xcrun -sdk iphoneos clang -rewrite-objc main.m -o main.cpp:(真機(jī)) xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 源文件名 -o 輸出的cpp文件名

    2024年02月16日
    瀏覽(18)
  • macos搭建appium-iOS自動化測試環(huán)境

    macos搭建appium-iOS自動化測試環(huán)境

    目錄 準(zhǔn)備工作 安裝必需的軟件 安裝appium 安裝XCode 下載WDA工程 配置WDA工程 搭建appium+wda自動化環(huán)境 第一步:啟動通過xcodebuild命令啟動wda服務(wù) 分享一下如何在mac電腦上搭建一個完整的appium自動化測試環(huán)境 前期需要準(zhǔn)備的設(shè)備和賬號: mac電腦一臺 iphone一臺 蘋果開發(fā)者賬號一

    2024年02月13日
    瀏覽(22)
  • uniapp打包之配置MacOS虛擬機(jī)生成iOS打包證書

    uniapp打包之配置MacOS虛擬機(jī)生成iOS打包證書

    uniapp是一款跨端開發(fā)框架,可用于快速開發(fā)iOS、Android、H5等多端應(yīng)用。本文將詳細(xì)介紹如何實現(xiàn)uniapp開發(fā)的iOS應(yīng)用打包。 一、下載蘋果原版鏡像文件 點擊此處下載 二、安裝VMware uniapp打包iOS應(yīng)用需要生成相應(yīng)證書和P2文件,這些都需要用到IOS環(huán)境,這里我是使用的是MacOS虛擬機(jī)

    2024年02月12日
    瀏覽(18)
  • 探索iOS之Metal編程指南

    探索iOS之Metal編程指南

    iOS推出Metal渲染庫為了取代OpenGL。Metal有自己的Shader語言,渲染效率比OpenGL高。在這里我們一起探索:Metal使用C++限制、預(yù)處理定義、動態(tài)鏈接配置、GPU編譯配置、設(shè)備坐標(biāo)系、視口坐標(biāo)系、紋理坐標(biāo)系、矢量類型、矩陣類型、采樣器狀態(tài)、矩陣相乘。 1、使用C++限制 C++14的一

    2024年02月08日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包