iOS 常用面試題

1,對于OC的動態(tài)性你有什么理解

  • 動態(tài)綁定
    在運行時而不是在編譯時確定方法的調用和函數的應用,通過msg send 實現動態(tài)方法的綁定
  • 動態(tài)類型
    對象的類型可以在運行時確定而不是編譯時固定,使用id 類型是可以在運行時決定對象的實際類型,通過 iskindof respondsToSelector 確保類型的安全執(zhí)行
  • 動態(tài)加載
    可以在運勢是按需加載動態(tài)庫或者框架
  • 動態(tài)方法交換
    可以通過class_replaceMethod 可以在運行時替換對象的特定方法,還以為類或者對象添加方法,擴展功能
  • 支持元編程
    以在運行時檢查和操作代碼的結構行為和類型,主要表現為
    -- 1.反射
    類反射,可以在運行時函數檢查類的信息。例如objc_getclasslist獲取當前進程中的類,class_getName 獲取類的名稱
    方法反射,使用class_copyMethodlist 獲取一個類的所有方法
    屬性反射,使用class_copyproertylist獲取類的屬性列表
    --2.動態(tài)方法的添加和替換
    class_addMethod, 為一個類新增方法,便于擴展
    class_replaceMedhtod,對方法的實現進行替換,實現對方法的攔截或修改
    perfromSelector 等動態(tài)調用方法
    --3.動態(tài)類的創(chuàng)建和修改
    ocj_allocateclasspair objc_registercallpair
    --4.動態(tài)屬性的操作,包括添加刪除修改
    class_addProperty 添加新的屬性
    objc_getAssociatedObject objc_setAssiciateObject 動態(tài)獲取和設置

2,有那些與動畫卡幀相關的問題

1.cpu 過載

復雜的計算,大量的數據處理,同步任務的阻塞主線程造成,cpu處理過多的計算任務,導致動畫無法按照預期幀率進行

  • 通過減少主線程的負載,將計算任務和數據處理移到后臺線程
  • 使用異步方法處理長時間人任務,避免阻塞主線程
  • 減少不必要的計算,使用緩存和預處理等方式

2.GPU 過載

高復雜的圖形操作,紋理資源過多,過度繪制等

  • 避免過度繪制,確保UI層次結構簡單
  • 減少復雜的圖形操作,優(yōu)化圖形資源的使用
  • 使用合適的圖形框架和技術,比如metal和core aniamation ,提高渲染效率

3.內存壓力

過多的內存分配,內存的釋放不及時,以及資源的泄漏等

  • 減少內存的分配,及時釋放資源
  • 使用autoreleasepool 控制內存使用,避免內存泄漏
  • 確保圖形資源的的有效管理,避免不必要紋理和緩存

4.主線程的阻塞

長時間的同步任務,網絡請求,文件的I/O 等

  • 避免在主線程進行同步操作,比如網絡請求,文件的IO等
  • 將主線程任務移動到后臺線程
  • 使用GCD是處理異步任務等

5.過度繪制 以及一些錯誤的動畫參數

復雜的視圖層級,冗余的UI元素等,一次錯誤的參數導致的沖突等

  • 確保參數的合理,避免動畫時間過長或過短
  • 避免動畫沖突,確保動畫的順序和邏輯
  • 使用合適的動畫工具,如core Animaiton uikit 動畫等,確保動畫流暢

使用instruments 進行性能分析
選擇合適的模板

  • time profiler:用于分析CPU時間的消耗 查看方法或函數的對cpu時間的占用
  • Acitvey monitor 監(jiān)控cpu和內存的總體使用情況
  • allocactions 分析內存分配,查看內存泄漏和過度分配
  • leaks 檢測內存泄漏
  • core animation 分析動畫的性能,查找動畫卡針的原因
  • gpu Frame Capture 分析gpu 性能適合渲染和圖形相關的性能分析

3.你知道線程?;畹姆绞絾?/h2>

線程?;钍侵副3志€程在一段時間內持續(xù)運行,以便能夠快速響應任務或請求。在 iOS 中,線程的創(chuàng)建和銷毀是具有一定成本的。如果應用頻繁創(chuàng)建和銷毀線程,可能會導致性能下降。因此,使用線程保活技術,可以提高應用的性能和響應速度。

iOS 中線程?;畹姆绞?/h5>
  • Grand Central Dispatch(GCD)提供了全局隊列,允許你將任務分派到系統(tǒng)級線程池。這些線程可以在需要時自動擴展和收縮,并在沒有任務時保持活動狀態(tài)。這是一種高效的線程?;罘绞健?/li>
  • OperationQueue 是一種高級的任務調度方式??梢酝ㄟ^設置隊列的最大并發(fā)數和任務等待時間,實現線程?;?。
  • 通過創(chuàng)建自定義線程,并讓線程在特定的條件下保持活動,可以實現線程?;睢_@通常通過使用 Run Loop 來維持線程的活動狀態(tài)。

在使用中需要注意

  • 資源的管理 避免浪費和泄漏
  • 線程的安全
  • 避免阻塞主線程

4.你使用過哪些鎖,能具體介紹一下嗎

首先鎖是在多線程環(huán)境下,防止數據競爭確保線程同步的一種方式。
常用的鎖包括

  • NSLock 適用于簡單的通途,是一個互斥鎖,適用于防止多個線程同時訪問共享資源
  • NSRecursiueLock 如果涉及遞歸操作或者多次獲取,使用的遞歸鎖,允許同一線程多次獲取不會應發(fā)死鎖
  • NSCondition NSConditionLock,適用于基于條件進行同步時 適合生產和消費的模型
    使用wait 或者 waituntilDate 等待條件滿足,使用sigal 或者boardcast通知繼續(xù)執(zhí)行
#import <Foundation/Foundation.h>

NSCondition *condition = [[NSCondition alloc] init];
BOOL ready = NO;

// 生產者線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [condition lock];
    ready = YES;  // 生產者準備好了
    [condition signal];  // 通知等待的線程
    [condition unlock];
});

// 消費者線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [condition lock];
    while (!ready) {  // 等待條件滿足
        [condition wait];
    }
    // 執(zhí)行消費者任務
    // ...
    [condition unlock];
});

NSconditionlock適合更復雜的條件控制
lockWhenCondition
unlockWithCondition

#import <Foundation/Foundation.h>

NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:0];

// 線程 1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lockWhenCondition:0];  // 等待條件為 0
    // 執(zhí)行操作
    [conditionLock unlockWithCondition:1];  // 設置條件為 1
});

// 線程 2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lockWhenCondition:1];  // 等待條件為 1
    // 執(zhí)行操作
    [conditionLock unlock];  // 釋放鎖
});

  • os_unfair_lock 高性能的無偏鎖,通常是替代就的OSSPinLock
#import <os/lock.h>

os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;

os_unfair_lock_lock(&lock);  // 獲取鎖
// 執(zhí)行操作
os_unfair_lock_unlock(&lock);  // 釋放鎖

  • dispatch Semaphore 需要控制線程并發(fā)
#import <Foundation/Foundation.h>

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);  // 初始信號量為 1

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  // 等待信號量
    // 執(zhí)行操作
    dispatch_semaphore_signal(semaphore);  // 釋放信號量
});

  • @synchronized
    oc 的同步語法糖
- (void)threadSafeMethod {
    @synchronized(self) {  // 加鎖
        // 執(zhí)行線程安全的操作
        // ...
    }  // 解鎖
}

5.多種計時器的使用有哪些問題

ios開發(fā)中最常用的計時器nstimer 需要注意的問題,

  • 確保在不需要時通過 invalidate() 來停止計時器
  • 當timer 作為閉包進行回調時,注意循環(huán)引用
  • 默認情況下,time 是在起創(chuàng)建runloop上運行,如果對應的runloop 被阻塞或者沒有運行會導致計時器不工作
  • nstimer 不是實時精確地,受到其runloop中的其他任務的影響,考慮精度可以使用 dispatchSourceTimer 或者CADisplaylink
  • 如果設置的時間間過短 可能會導致性能下降
  • 避免在后臺運行計時器,可能會被系統(tǒng)中止

立即運行:如果需要計時器立即開始運行,使用 scheduledTimerWithTimeInterval(:target:selector:userInfo:repeats:) 或 scheduledTimerWithTimeInterval(:repeats:block:)。
會立即將計時器添加到當前的runloop 中,并開始執(zhí)行

稍后運行:如果需要稍后啟動計時器或添加到特定的 RunLoop,使用 init(timeInterval:target:selector:userInfo:repeats:) 或 init(fire:interval:target:selector:userInfo:repeats:)

dispatchsouretimer 是基于GCD的定時器,用于更精確的定時任務,不受到runloop的影響,尤其在多線程環(huán)境中使用,需要注意手動管理生命周期,以及注意UI刷新任務需要回調到主線程執(zhí)行

let queue = DispatchQueue(label: "com.myapp.timerQueue")
let dispatchTimer = DispatchSource.makeTimerSource(queue: queue)

// 配置定時器
dispatchTimer.schedule(deadline: .now() + 2.0, repeating: 2.0) // 2秒后開始,每2秒執(zhí)行

// 添加回調
dispatchTimer.setEventHandler { [weak self] in
    self?.doSomething()
}

// 啟動定時器
dispatchTimer.resume() // 默認處于暫停狀態(tài),必須 resume 才會開始
//DispatchSourceTimer 需要手動取消,確保計時器不會泄漏。
dispatchTimer.cancel() // 取消計時器

CADisplayLink 用戶屏幕刷新通的任務常于動畫或者視圖渲染,由于與屏幕刷新同步資源消耗高,不適用于延遲執(zhí)行

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self
                                                         selector:@selector(updateFrame)];

// 將 DisplayLink 添加到 RunLoop
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

[displayLink invalidate]; // 停止 DisplayLink
displayLink = nil; // 確保清除引用

6.如何檢測bug和崩潰,包括線上的問題

1.bugly分析 符號表分析 在xcode編譯過程中。編譯器會生成一個符號表,包含了所有代碼中的符號名稱和地址映射,在使用時可以講崩潰日志中的地址信息轉換為人類刻度的信息,例如函數名稱 文件名等 能都快速定位
2.日志分析
3.異常捕獲
4.功能限制開關
5.xcode 調試,debug instrments

7.你有哪些使用KVO 和 KVC的經驗?

kvo key-value observing

是一種觀察屬性變化的機制,能夠在對象屬性變化時接收到通知,分為3個步驟
1.注冊觀察者

[self addObserver:self forKeyPath:@"propertyName" options:NSKeyValueObservingOptionNew context:nil];

2.觀察屬性變化

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"propertyName"]) {
        NSLog(@"Property changed: %@", change[NSKeyValueChangeNewKey]);
    }
}

3.取消注冊

[self removeObserver:self forKeyPath:@"propertyName"];

使用的是oc 的運行時動態(tài)特性,主要實現步驟為,當你為某個對象添加觀察者時,系統(tǒng)會創(chuàng)建對應的子類,并且重寫其setter方法,在調用原setter 方法之前發(fā)出通知

kvc key-value-codeing

是一種通過鍵訪問屬性的方法,允許使用字符串鍵來獲取和設置對象的屬性
通過valueforkey setvalue forkey 來實現
還支持嵌套路徑

NSString *city = [myObject valueForKeyPath:@"address.city"];

底層實現
1,動態(tài)方法查找,查找是否有常規(guī)的getter( get<Key>、is<Key>、<Key>) 和 setter 方法
2,訪問實例變量,如果類允許的話
3,valueForUndefinedKey setValue:forUndefinedKey:
accessInstanceVariablesDirectly 決定是否能直接訪問實例變量
如果鍵是以 countof enumertorof memberof 等前綴命名,kvc 會假設這是一個集合,并調用相關方法

8,除了加鎖,多線程的安全問題還有哪些

1,多個線程同時訪問修改共享數據,可能導致數據不一致的問題,解決辦法可以通過加鎖
2,死鎖,多個線程相互等待導致的無法繼續(xù),大概是因為鎖的使用不當
3,資源競爭,多個線程競爭有限的系統(tǒng)資源(如內存、文件句柄等)可能導致性能下降或者死鎖。
4,內存管理問題,多個線程訪問數據,一個線程可能釋放了另一個線程仍在使用的內存,導致內存訪問錯誤或崩潰。
5,頻繁的線程切換可能會帶來性能開銷
考慮使用合適的同步機制,鎖定策略,原子操作(原子操作是一種不可分分割的操作,一單開始就會完成執(zhí)行,在多線程中其他線程無法終端或者干預這個操作),確保線程的正確和資源的管理

9.什么是runloop ,你怎么理解他,他有什么用處

runloop 還是一個不斷循環(huán)的機制,負責處理應用程序中的各種事件,每個線程有一個runloop.
runloop 循環(huán)包括三個階段

  • 等待事件, RL進入休眠,等待事件,不占資源
  • 處理事件,有事件發(fā)生,喚醒并處理
  • 回到等待
    runloop 和線程的關系
  • 每條線程都有唯一一個與之對應的runloop
  • runloop 保存在一個全局字典中,key是線程,value 是runloop
  • 線程創(chuàng)建是沒有runloop 對象,只有在第一次獲取時才能創(chuàng)建 [NSRunLoop currentRunLoop]
  • runloop 會在線程結束是銷毀
  • 主線程的runloop 已經自動穿件,子線程默認沒有開啟

應用

  • 線程?;?/li>
  • 解決nstimer 滑動式停止的問題
    nstimer 默認綁定到 NSDefaultRunLoopMode,當用戶滾動視圖時,runloop 會切換到 UITrackingRunloopModel ,解決辦法是將timer添加到NSRunLoopCommonModes
  • 監(jiān)控應用卡頓
    runloop一共有多個狀態(tài)其中包括
1.kCFRunLoopEntry 這個表示Runloop 剛剛開始運行,還沒處理時間準備好進入循環(huán)了
可以進行一些初始化操作,或者設置一些環(huán)境
2.kCFRunLoopBeforeTimers 這個狀態(tài)表示即將檢查計時器,timers,可以在這里檢查或者調整計時器確保正確的時間間隔
3.kCFRunloopBeforeSources  這個表示即將處理輸入源,包括用戶 系統(tǒng)時間,在這里可以設置或者修改輸入源
4.kCFRunloopBeforeWaitng 這個表示即將進去休眠,這里可以執(zhí)行一些非緊急任務,
5.kCFRunLoopAfterWaiting 這個表示剛剛從休眠中喚醒,可以在這里執(zhí)行一些準備操作
6.KCFRunLoopExit 這個表示已經完成循環(huán),即將推出或者開始新的循環(huán),可以在這里做一些清理操作

使用 RunLoop 觀察者來監(jiān)控 RunLoop 的狀態(tài)。當 RunLoop 在 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting 之間的時間超過超時時間時,表明應用可能出現卡頓。

NSTimeInterval timeoutInterval = 0.5; // 超時時間
CFRunLoopObserverRef observer;
CFRunLoopActivity activities = kCFRunLoopBeforeSources | kCFRunLoopAfterWaiting;

observer = CFRunLoopObserverCreateWithHandler(NULL, activities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    static NSDate *entryTime = nil;

    if (activity == kCFRunLoopBeforeSources) {
        entryTime = [NSDate date]; // 記錄 RunLoop 進入狀態(tài)的時間
    } else if (activity == kCFRunLoopAfterWaiting) {
        if (entryTime && -[entryTime timeIntervalSinceNow] > timeoutInterval) {
            NSLog(@"Application is stuck"); // 如果超過超時間,表示可能卡頓
        }
    }
});

CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);

  • 性能優(yōu)化
    RunLoop 即將休眠時執(zhí)行一些非緊急任務,以確保這些任務不會阻塞主線程。這有助于提高應用的響應能力。
CFRunLoopObserverRef observer;

observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    // 在 RunLoop 即將休眠前執(zhí)行一些任務
    [self performBackgroundTasks];
});

CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);

10,你有使用Coredata的經驗嗎,可以分享一下你的使用體驗和注意事項嗎

ios開發(fā)中的數據持久包括NSUserDefault 文件存儲,SQlite ,Core Data,Keychain等,

  • userdefault
    一般用于存儲用的配置等小型,使用簡單,不適用于大量數據或者復雜的數據結構
  • 文件存儲
    可以直接將數據保存到文件系統(tǒng)中,通常用于存儲比較大的數據 圖片等,缺點需要手動管理文件名和路徑等,不適合管理復雜的數據結構
  • SQLite
    輕量數據庫,支持SQL語法,適合存儲結構化數據和執(zhí)行復雜的查詢。需要熟悉SQL語句和數據庫操作,手動管理數據庫的鏈接和事務
  • Keychain
    用于存儲銘感數據的安全方式,適合存儲密碼和秘鑰等,有點就是數據加密且有系統(tǒng)級別的安全,應用刪除后任可保留,可以在不同應用之間共享,使用比較復雜,不適合大量數據
  • coredata
    iOS 上的持久化框架,提供了高級的數據管理功能,適合復雜的數據結構和關系映射,還支持數據遷移和批處理,查詢和過濾功能
    在使用上需要
    1.首先需要配置coredata 棧,通常會創(chuàng)建一個單例,來負責配置和管理core data棧,使用NSPersistentContainer 初始化,并且提供viewcontext 來操作
    2.執(zhí)行操作
    創(chuàng)建數據,需要創(chuàng)建一個NSManagerObject 對象并且插入到NSManagerObjectContext中
    讀取數據,使用NSFetchRequest進行查詢
    更新數據,需要先查詢到對象,在修改屬性并保存
    刪除數據,需要先查詢對象,再從上下文中刪除 并保存

需要注意的是在多線程環(huán)境中,每個線程需要使用獨立的NSManagerObjectContext,不要共享以防止錯誤
在設置實體的關聯的時候,需要注意避免循環(huán)引用

11,你知道哪些性能優(yōu)化的方法,能具體介紹實現原理嗎

  • 內存優(yōu)化
    內存分配需要系統(tǒng)進行內存管理和垃圾收集,頻繁的內存分配會增加系統(tǒng)負擔
    可以通過復用例如tableview collectview ,不要頻繁的創(chuàng)建一些變量,及時的釋放資源,對數據進行一些緩存等
    適當的使用autoreleaseppol 在循環(huán)或者后臺線程中需要創(chuàng)建大量的的零時變量時使用,避免內存的過度增長
  • cpu使用優(yōu)化
    減少循環(huán)和冗余計算,嘗試使用更高效的算法,降低計算復雜度,避免不必要的循環(huán),地丟等
    將一些耗時操作放到后臺線程,避免主線程的阻塞,充分發(fā)揮多核處理器的優(yōu)勢
    使用instruments分析方法函數的調用時間
  • ui渲染優(yōu)化
    減少重繪次數,延遲加載不急需的UI元素,對圖片或者其他資源按需加載
    使用instruments工具檢測離屏渲染,避免過度使用 圓角 陰影等特性
    減少負責層級的試圖層級機構 合并重復試圖
    使用shouldResterize 進行緩存
  • 網絡性能優(yōu)化
    合并小的網絡請求,肩上網絡通信次數,使用批量請求和數據壓縮來優(yōu)化網絡
    將頻繁訪問的數據緩存在本地,避免重復請求
    確保網絡請求在后臺線程執(zhí)行,避免主線程的的阻塞
  • 文件或者數據庫的優(yōu)化
    避免頻繁的讀寫文件,對SQL或者coredata 等持久化的數據,使用事務和批處理來優(yōu)化性能,減少查詢次數,使用索引等來優(yōu)化查詢
  • 使用instrument或者日志來,分析性能 具體問題具體解決

12,如何檢測卡頓,能介紹具體實現原理嗎

  • 上文提到的runloop
  • 主線程中添加timer 定期檢查時間間隔是否大于預期
  • 使用instruments 分析工具分析
  • 或者一些第三方工具

14,內存占用過高的檢測方法是什么,你能介紹具體實現原理嗎,根據你的項目經驗,可以分享一下你項目中的難點和優(yōu)點,以及架構設計的優(yōu)缺點嗎

instruments中的內存分析工具,包括memory leaks (主要是通過引用計數和生命周期,判斷其引用計數為0的時候,是否被釋放,),allocaction等工具
Xcode debug模式下可以直接查看內存使用
使用建議,正確的使用weak 避免循環(huán)引用和內存泄漏,在執(zhí)行大量的臨時對象創(chuàng)建時,使用autoreleasepool 來及時釋放內存,在試圖頁面消失時,檢測內存變化,對應的對象dealloc 方法是否執(zhí)行等

15,多線程的實現方式,GCD和NSOperation的優(yōu)缺點

常見的就是gcd NSOperation 還有NSThread
GCD是通過隊列和任務來實現的并發(fā)操作,他的優(yōu)點包括高性能,簡單易用,使用靈活
缺點是缺乏高級功能調試難度較高
GCD的是建立才GCD之上的,提供了更加豐富的功能入任務的依賴關系,優(yōu)先級,任務的取消等,比GCD相對復雜,由于支持了更多特性,性能略低于GCD

16,你知道哪些第三方庫,你有看過哪些源碼,他們使用了哪些技術,他們有什么優(yōu)缺點,他們的架構設計師是怎么樣的

  1. AFNetworking
    AFNetworking 是最流行的 Objective-C 網絡庫之一,簡化了 HTTP 網絡請求和響應處理。

使用的技術

  • 基于 NSURLSession:AFNetworking 在 NSURLSession 之上構建,提供更高級的功能。

  • 操作隊列:使用 NSOperationQueue 來管理并發(fā)網絡請求。

  • 序列化:支持 JSON、XML、圖片等多種數據格式的序列化和反序列化。
    優(yōu)缺點

  • 優(yōu)點:強大的網絡功能,支持鏈式調用,易于使用,社區(qū)支持廣泛。

  • 缺點:隨著 Swift 的流行,其 Objective-C 版本的維護力度下降,但仍有許多應用依賴于此。
    架構設計
    AFNetworking 的架構包括以下主要部分:

  • AFHTTPSessionManager:處理網絡請求和響應,基于 NSURLSession。

  • AFURLSessionManager:處理低級別的 URLSession 操作。

  • AFSecurityPolicy:提供 SSL/TLS 安全策略。

  • AFImageDownloader:用于異步下載圖片。

  1. SDWebImage
    SDWebImage 是一個流行的圖片加載和緩存庫,常用于優(yōu)化圖片處理。

使用的技術

  • 異步加載:支持異步下載和緩存圖片。

  • 緩存機制:提供內存和磁盤緩存,減少網絡請求。

  • 圖像解碼:優(yōu)化圖像解碼以提高性能。
    優(yōu)缺點

  • 優(yōu)點:簡單易用,提供豐富的功能,如圖片緩存、異步加載等。

  • 缺點:可能會增加內存占用,特別是在處理大量圖片時。
    架構設計
    SDWebImage 的架構包括以下主要部分:

  • SDWebImageManager:管理圖片下載和緩存。

  • SDImageCache:提供內存和磁盤緩存。

  • SDWebImageDownloader:處理圖片的異步下載。

  • UIImageView+WebCache:為 UIImageView 提供擴展,支持直接加載網絡圖片。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容