「OC」源码学习——锁

iOS LiveCommunicationKit OC 实现 LiveCommunicationKit主要用于优化VoIP通话体验,适用于需要高质量语音通话功能的应用。类似的功能,允许应用程序设置为系统默认通话应用,但与CallKit不同的是,LiveCommunicationKit在锁屏状态下不会全屏弹出,也不会在。因为项目是OC版本的,所以进行了封装实现,目前已经实现OC版本,供大家使用。8.0.56版本就采用了LiveCommunicationKit接口,替换了原先的CallKit框架,以进一步提升语音通话体验‌。中引入的一个新框架,旨在优化。 阅读详情

「OC」源码学习——锁

锁的原因

在多线程编程中,锁的作用是确保共享资源的线程安全,防止多个线程同时修改同一数据导致的数据混乱、逻辑错误甚至程序崩溃。

@interface TicketManager : NSObject
@property (nonatomic, assign) NSInteger ticketCount;
@end

@implementation TicketManager
- (void)sellTicketUnsafe {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            if (self.ticketCount > 0) {
                [NSThread sleepForTimeInterval:0.1]; // 模拟耗时操作
                self.ticketCount--;
                NSLog(@"窗口A售出1张票,剩余:%ld", self.ticketCount);
            } else {
                NSLog(@"票已售罄");
                break;
            }
        }
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            if (self.ticketCount > 0) {
                [NSThread sleepForTimeInterval:0.1];
                self.ticketCount--;
                NSLog(@"窗口B售出1张票,剩余:%ld", self.ticketCount);
            } else {
                break;
            }
        }
    });
}
@end

image-20250527220531829

我们可以发现售出的票基本都是乱序的,也就是说访问的时候,不同线程访问同个内容出现了问题,这时候就需要锁来规范顺序

image-20250527221959066

锁的种类

自旋锁

线程会反复检查变量是否可用。由于线程这个过程中一致保持执行,所以是一种忙等待。 一旦获取了自旋锁,线程就会一直保持该锁,直到显式释放自旋锁。自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

互斥锁

互斥锁通过阻塞线程实现资源独占访问,确保临界区代码的原子性。

普通的互斥锁不支持递归,当我们在锁之中尝试重复加锁,会造成死锁。当线程首次调用lock()方法时,锁会被标记为“已占用”;若该线程未释放锁(未调用unlock())就再次调用lock(),系统会强制线程进入阻塞状态,等待锁释放。

递归锁

递归锁是特殊的互斥锁,允许同一线程多次加锁,避免嵌套死锁。

针对这种情况我们可以用 @synchronized 来解决,也可以用 NSRecursiveLock 来解决。但是 NSRecursiveLock是不支持多线程的执行

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

        // 线程1:递归加锁
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^block)(int);
            block = ^(int value) {
                [lock lock];
                if (value > 0) {
                    NSLog(@"线程1: value=%d", value);
                    [NSThread sleepForTimeInterval:0.5];
                    block(value - 1);
                }
                [lock unlock];
            };
            block(3);
        });

        // 线程2:并发加锁
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [lock lock];
            NSLog(@"线程2: 获取锁成功");
            [lock unlock];
        });

image-20250528131221998

可以看到原本多线程的任务,这个代码只完成了一次

条件锁

条件锁就是条件变量,当进程的某些资源要求不满足时就进入休眠,即锁住了,当资源被分配到了,条件锁打开了,进程继续运行

@interface StageTaskExample : NSObject
@property (nonatomic, strong) NSConditionLock *conditionLock;
@end

@implementation StageTaskExample

- (instancetype)init {
    self = [super init];
    if (self) {
        // 初始条件值为0
        _conditionLock = [[NSConditionLock alloc] initWithCondition:0];
    }
    return self;
}

// 阶段1:初始化
- (void)stage1 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.conditionLock lock];
        NSLog(@"阶段1:初始化完成");
        [self.conditionLock unlockWithCondition:1]; // 解锁并设置条件值为1
    });
}

// 阶段2:加载数据
- (void)stage2 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.conditionLock lockWhenCondition:1]; // 等待条件值为1
        NSLog(@"阶段2:数据加载完成");
        [self.conditionLock unlockWithCondition:2]; // 解锁并设置条件值为2
    });
}

// 阶段3:渲染界面
- (void)stage3 {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self.conditionLock lockWhenCondition:2]; // 等待条件值为2
        NSLog(@"阶段3:界面渲染完成");
        [self.conditionLock unlock];
    });
}
@end

锁的性能

img

synchronized原理

通过汇编语言我们可以发现,在调用@ synchronized的时候会走底层的objc_sync_enter 和 objc_sync_exit方法

objc_sync_enter

int objc_sync_enter(id obj)
{
    int result = _objc_sync_enter_kind(obj, SyncKind::atSynchronize);
    if (result != OBJC_SYNC_SUCCESS)
        OBJC_DEBUG_OPTION_REPORT_ERROR(DebugSyncErrors,
            "objc_sync_enter(%p) returned error %d", obj, result);
    return result;
}

int _objc_sync_enter_kind(id obj, SyncKind kind)
{
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
        SyncData* data = id2data(obj, kind, ACQUIRE);
        ASSERT(data);
        data->mutex.lock();
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
        if (DebugNilSync == Fatal)
            _objc_fatal("@synchronized(nil) is fatal");
    }

    return result;
}

objc_sync_exit

int objc_sync_exit(id obj)
{
    int result = _objc_sync_exit_kind(obj, SyncKind::atSynchronize);
    if (result != OBJC_SYNC_SUCCESS)
        OBJC_DEBUG_OPTION_REPORT_ERROR(DebugSyncErrors,
            "objc_sync_exit(%p) returned error %d", obj, result);
    return result;
}

int _objc_sync_exit_kind(id obj, SyncKind kind)
{
    int result = OBJC_SYNC_SUCCESS;
    
    if (obj) {
        SyncData* data = id2data(obj, kind, RELEASE);
        if (!data) {
            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
        } else {
            bool okay = data->mutex.tryUnlock();
            if (!okay) {
                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
            }
        }
    } else {
        // @synchronized(nil) does nothing
    }
	

    return result;
}

我们从源码可以看出来,无论是进还是出,其实本质上都调用id2data

id2data

// 核心函数:根据对象和同步类型获取/创建同步数据(SyncData)
static SyncData* id2data(id object, SyncKind kind, enum usage why)
{
    ASSERT(kind != SyncKind::invalid); // 同步类型有效性检查
    spinlock_t *lockp = &LOCK_FOR_OBJ(object); // 获取对象关联的自旋锁指针
    SyncData ​**listp = &LIST_FOR_OBJ(object);  // 获取对象关联的SyncData链表头指针
    SyncData* result = NULL; // 存储最终找到/创建的SyncData

#if ENABLE_FAST_CACHE // 快速缓存功能开关
    // 步骤1:检查线程本地存储(TLS)快速缓存
    bool fastCacheOccupied = NO;
    SyncData *data = syncData; // TLS中缓存的SyncData指针(线程最近使用的锁)
    if (data) {
        fastCacheOccupied = YES;

        if (data->matches(object, kind)) { // 检查对象和同步类型是否匹配
            result = data;
            // 有效性验证:线程计数和锁计数必须>0(防逻辑错误)
            if (result->threadCount <= 0  ||  syncLockCount <= 0) {
                _objc_fatal("id2data fastcache is buggy");
            }

            // 根据操作类型更新锁计数
            switch(why) {
            case ACQUIRE: { // 加锁操作
                ++syncLockCount; // 递增TLS中的嵌套锁计数
                break;
            }
            case RELEASE: // 解锁操作
                if (--syncLockCount == 0) { // 锁计数归零时清理缓存
                    syncData = nullptr;     // 清空TLS缓存
                    AtomicDecrement(&result->threadCount); // 原子减少线程计数
                }
                break;
            case CHECK: // 检查操作(无状态变更)
                break;
            }
            return result; // 快速返回已缓存的SyncData
        }
    }
#endif // ENABLE_FAST_CACHE

    // 步骤2:检查线程本地缓存(SyncCache)
    SyncCache *cache = fetch_cache(NO); // 获取当前线程的缓存池(不自动创建)
    if (cache) {
        for (unsigned int i = 0; i < cache->used; i++) { // 遍历缓存条目
            SyncCacheItem *item = &cache->list[i];
            if (!item->data->matches(object, kind)) continue;

            result = item->data;
            // 有效性检查(同上)
            if (result->threadCount <= 0  ||  item->lockCount <= 0) {
                _objc_fatal("id2data cache is buggy");
            }
                
            switch(why) { // 更新锁计数逻辑
            case ACQUIRE:
                item->lockCount++; // 增加缓存条目中的锁计数
                break;
            case RELEASE:
                if (--item->lockCount == 0) { // 锁计数归零时清理
                    cache->list[i] = cache->list[--cache->used]; // 用最后条目覆盖当前
                    AtomicDecrement(&result->threadCount); // 原子减少线程计数
                }
                break;
            case CHECK:
                break;
            }
            return result; // 返回缓存中的SyncData
        }
    }

    // 步骤3:全局锁保护下的全局链表操作
    lockp->lock(); // 获取自旋锁(防止多线程竞争)

    {
        SyncData* p;
        SyncData* firstUnused = NULL; // 用于复用空闲SyncData节点
        // 遍历全局链表查找匹配项
        for (p = *listp; p != NULL; p = p->nextData) {
            if (p->matches(object, kind)) { // 找到匹配的SyncData
                result = p;
                AtomicIncrement(&result->threadCount); // 原子增加线程计数
                goto done; // 跳转到解锁和缓存处理
            }
            // 记录第一个未使用的SyncData(内存复用优化)
            if (!firstUnused && p->threadCount == 0) firstUnused = p;
        }
    
        // 未找到且非释放/检查操作时创建新节点
        if (why == ACQUIRE) {
            result = firstUnused ? firstUnused : (SyncData*)posix_memalign(...); // 复用或分配内存
            result->object = (objc_object *)object; // 绑定对象
            result->kind = kind;                    // 设置同步类型
            result->threadCount = 1;                // 初始化线程计数
            new (&result->mutex) recursive_mutex_t(fork_unsafe); // 初始化递归互斥锁
            result->nextData = *listp;              // 插入链表头部
            *listp = result;
        }
    }
    
 done:
    lockp->unlock(); // 释放自旋锁

    // 步骤4:缓存新获取的SyncData
    if (result && why == ACQUIRE) {
#if ENABLE_FAST_CACHE
        if (!fastCacheOccupied) { // 若TLS未占用则存入
            syncData = result;    // 存储到TLS
            syncLockCount = 1;    // 初始化锁计数
        } else 
#endif
        { // 否则存入线程缓存
            if (!cache) cache = fetch_cache(YES); // 不存在则创建线程缓存
            cache->list[cache->used++] = {result, 1}; // 添加新条目
        }
    }

    return result; // 返回最终的SyncData
}

调用流程

┌──────────────────────┐
│      调用入口         │
└──────────┬───────────┘
           ↓
┌──────────────────────┐
│ 第一步:TLS快速缓存查找 │
└──────────┬───────────┘
           ├─ 成功 → 更新 lockCount 并返回 SyncData
           ↓
┌──────────────────────┐
│ 第二步:SyncCache线程缓存│
└──────────┬───────────┘
           ├─ 成功 → 更新 lockCount 并返回 SyncData
           ↓
┌──────────────────────┐
│ 第三步:全局链表操作      │
└──────────┬───────────┘
           ├─ 创建/复用 SyncData → 更新缓存
           ↓
┌──────────────────────┐
│      返回 SyncData     │
└──────────────────────┘

那么TLS和程序之中的SyncCache 有什么区别呢?为什么需要先查找TLS再查找SyncCache呢?

维度TLS(线程局部存储)SyncCache(同步缓存)
存储内容单个线程最近使用的锁对象(SyncData)及锁计数(lockCount)同一线程持有的多个锁对象(SyncData链表)及各自的锁计数
访问速度O(1) 直接访问(通过线程控制块快速定位)O(n) 需遍历数组(速度稍慢但支持多锁管理)
生命周期与线程绑定,线程销毁时自动释放;锁计数归零时主动清理锁计数归零时动态移除条目,缓存池复用SyncData节点
适用场景高频单锁递归(如嵌套@synchronized)多锁交替使用(如循环内对不同对象加锁)
数据结构单条目快速缓存(仅存储最近使用的SyncData)动态数组(存储多个SyncCacheItem,每个条目包含SyncData指针和lockCount)
查找步骤分析
  1. TLS 快速缓存(Thread Local Storage)

    • 作用:存储线程 最近一次使用的锁对象(SyncData)及其 嵌套锁计数(lockCount)。
    • 操作逻辑:
      • 查找:通过 tls_get_direct 获取线程绑定的 SyncData。
      • 匹配条件:检查 data->object 是否与当前对象匹配。
      • 计数更新:
        • ACQUIRE(加锁):lockCount++,更新 TLS 缓存。
        • RELEASE(解锁):lockCount--,若归零则移除 TLS 缓存并原子递减 threadCount。
      • 错误处理:若 threadCount ≤ 0 或 lockCount ≤ 0,触发崩溃。
  2. SyncCache 线程缓存

    • 作用:存储线程 已持有的多个锁对象,支持多锁交替使用。
    • 操作逻辑:
      • 查找:通过 fetch_cache(NO) 获取线程缓存池,遍历 SyncCacheItem 数组。
      • 匹配条件:检查 item->data->object 是否匹配。
      • 计数更新:
        • ACQUIRE:item->lockCount++。
        • RELEASE:item->lockCount--,若归零则从数组中移除并原子递减 threadCount。
  3. 全局链表与 SyncData 创建

    • 数据结构:
      • SyncList:全局哈希表结构,通过 sDataLists 管理所有 SyncData 节点。
      • SyncData:链表节点,含 object(锁对象)、threadCount(线程持有数)、mutex(递归锁)。
    • 操作逻辑:
      • 加锁保护:通过自旋锁 lockp->lock() 确保线程安全。
      • 链表遍历:查找匹配的 SyncData 或首个空闲节点(firstUnused)。
      • 节点创建:若未找到,通过 posix_memalign 分配内存,初始化并插入链表头部。
      • 缓存更新:新 SyncData 存入 TLS 或 SyncCache。
场景处理路径
首次加锁TLS → 未命中 → SyncCache → 未命中 → 全局链表创建 SyncData → 存入 TLS
同一线程递归加锁TLS 命中 → lockCount++
不同线程访问同一锁TLS → 未命中 → SyncCache → 未命中 → 全局链表匹配 → threadCount++

synchronized的雷点

 - (void)cjl_testSync{
    _testArray = [NSMutableArray array];
    for (int i = 0; i < 200000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            @synchronized (self.testArray) {
                self.testArray = [NSMutableArray array];
            }
        });
    }
}

我们运行代码会发现,程序会报错,我们来探究一下原因


锁对象的不稳定性(核心原因)

代码中 @synchronized 的锁对象是 self.testArray,但循环内每次异步任务都会对 self.testArray 重新赋值一个新的 NSMutableArray 实例。这导致以下问题:

  • 锁对象动态变化:每个线程可能使用不同的 testArray 实例作为锁对象,导致 同步失效。例如,线程A锁的是旧的 array1,线程B锁的是新的 array2,两者互不干扰,无法实现真正的互斥。
  • 全局链表管理冲突:@synchronized 底层通过全局哈希表管理锁对象(SyncData 链表)。若锁对象频繁变化,会触发大量 SyncData 节点的创建和销毁,导致内存管理异常或链表断裂。
解决方案
  1. 固定锁对象:
    使用一个 独立且稳定的对象 作为锁(如 self 或专用 NSObject 实例),避免锁对象动态变化:

    @synchronized (self) {
        self.testArray = [NSMutableArray array];
    }
    
  2. 控制线程并发量:
    改用串行队列或限制并发数(如 NSOperationQueue.maxConcurrentOperationCount),避免资源耗尽。

如何在OC中进行源码分析 Part01-获取objc-runtime的源码objc-runtime的源码地址 注意:⚠️⚠️⚠️,objc-runtime的源码的新旧程度并不是按照时间进行排序,而是按照包的大小进行的排序,一般可以理解为包最大的即为最新的源码 但是官网的runtime代码是无法直接运行的,如果想直接运行可以下载RetVal处理过的源代码 Part02-将OC代码编译成C++... 阅读详情

相关推荐

手把手教学:用国产GLM-4.6替代Claude4.0,实现AI自动化编程全流程

本文提供了一份详细的教程,指导开发者使用国产GLM-4.6模型替代Claude 4.0,实现AI自动化编程。内容涵盖从获取API密钥、搭建本地环境、改造Claude Code工具到实战开发与VSCode集成的全流程,旨在帮助用户低成本、高效率地体验AI自主编程。

weixin_29070861的博客 552

OC里的锁

iOS,OC,锁

wu347771769的专栏 1007

ArcGIS Pro实战:用时空立方体分析共享单车轨迹数据(含3D可视化技巧)

本文详细介绍了在ArcGIS Pro中利用时空立方体技术分析共享单车轨迹数据的全流程。从数据预处理、构建时空立方体,到执行新兴时空热点分析,并最终实现动态3D可视化,手把手教你挖掘城市骑行活动的时空规律。文章重点解析了工具参数设置、常见报错解决方案,并提供了可复用的Python脚本,助力城市规划与交通研究者高效完成时空模式挖掘。

weixin_28323057的博客 136

OC 各种线程锁

参考: 正确使用多线程同步锁@synchronized() iOS中的锁 iOS多线程安全详解 iOS 常见知识点(三):Lock #####各种锁 1、 简单常见的: (1)atomic (2)@synchronized 2、NS对象形式的: (1)NSLock (2)NSConditionLock条件锁 (3)NSRecursiveLock递归锁 (4)NSCondition挂起唤醒 3、G...

weixin_34174322的博客 434

OpenFOAM高效学习路线:如何用B站+东岳流体3个月掌握CFD仿真

本文分享了一套高效的OpenFOAM学习路线,结合B站视频、东岳流体教程和官方文档,帮助在职工程师在3个月内掌握CFD仿真技术。通过渐进式学习计划和案例训练,从基础到实战,快速提升独立建模能力。

weixin_29202213的博客 314

Unity UI适配核心:Canvas Scaler模式解析与2023版避坑指南

UI适配是游戏与应用开发中的基础挑战,其核心在于确保界面元素在不同屏幕分辨率与设备上保持视觉一致与功能可用。原理上,系统通过坐标转换与缩放计算,将设计稿的布局映射到物理屏幕。技术价值在于提升用户体验的一致性与产品的跨平台兼容性。应用场景广泛覆盖手游、PC端应用及各类交互界面。本文聚焦Unity引擎中的Canvas Scaler组件,深入剖析其Scale With Screen Size等模式的工作原理,并结合锚点设置、安全区域处理等高频搜索热词,提供解决UI错位、拉伸及异形屏适配等常见问题的工程实践方案,帮

weixin_30920597的博客 621

操作系统之进程与线程

目录1. 进程2. 线程3. 进程和线程的关系 1. 进程 概念:进程是指一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程,进程是资源分配的基本单位。 组成:包含了正在运行的一个程序的所有状态信息。 特点: 动态性:可动态地创建、结束进程。 并发性:进程可以被独立调度并占用处理机执行。 独立性:不同进程的工作不互相影响。 制约性:因访问共享数据/资源或进程间同步而产生制约。 进程与...

一枝会思想的芦苇的博客 249

C#实战:如何用winmm.dll打造毫秒级高精度定时器(附完整代码)

本文详细介绍了在C#中利用winmm.dll实现毫秒级高精度定时器的完整方案。针对.NET内置定时器精度不足的问题,通过调用timeSetEvent等原生API,提供了超越系统时钟中断周期的精准定时能力。文章包含核心原理剖析、完整的封装类代码以及音视频处理、游戏开发等场景下的实战应用与性能调优指南。

ujm5678901的博客 689

iOS 多线程 锁 互斥 同步

在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法) 会自动对参数对象加锁,保证临界区内的代码线程安全 @synchronized(self) { // 这段代码对其他 @synchronized(self) 都是互斥的 // self 指向同一个

程序人生 5万+

在PyCharm中配置使用Anaconda环境

在PyCharm中配置使用Anaconda环境一、步骤1.Anaconda2.PyCharm总结 一、步骤 1.Anaconda 如未安装Anaconda可至其官网下载,学习使用个人版就可以了。 下载地址:https://www.anaconda.com/products/individual 1、打开Anaconda Navigator,点击Environments可以看到anaconda中已经有了默认的“base”环境,其中包含了大多数我们会用到的包,在PyCharm中可以直接使用该环境,而无需自行下载

TuckX的博客 19万+

Qt —— 在Win11下“Qt for Android“开发环境,通过USB在真机(华为Meta60 PRO)上调试运行,并打包APK并在真实手机运行

Qt —— 在Win11下“Qt for Android“开发环境,打包APK并在真实手机运行

310

iOS - 互斥锁&&自旋锁 多线程安全隐患(转载)

一、多线程安全隐患 资源共享 一块资源可能会被多个线程共享,也就是多个线程可能会访问到一块资源比如多个线程访问同一个对象,同一个变量,同一个文件。当多线程访问同一块资源的时候,很容易引发数据错乱和数据安全问题二、原子和非原子属性1>OC 在定义属性的时候有nonatomic和atomic两种选择 *atomic:原子属性,为 setter 方法加锁 *non...

weixin_34345560的博客 330

复数值神经网络可能是深度学习的未来

复数这种东西,在人的头脑中似乎抽象、似乎复杂,然而,对于计算机来说,一点也不抽象,不复杂,那么,将复数概念推广到神经网络会是什么结果呢?本篇介绍国外的一些同行的尝试实践,请我们注意观察他们的进展。

gongdiwudu的专栏 6613

「OC」源码学习——类的加载

load和是两个常用的类方法,它们在不同的时间点被调用,并且有不同的用途。在消息转发的流程之中,有一个很重要的方法就是,其中使用了对懒加载类进行初始化,而经过一系列的调用其实就会调用的方法。

aa2002aa的博客 1101

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

「OC」源码学习——关联属性再探索

在寒假学习小蓝书的时候就已经接触过了关联属性,现在在学习源码的过程之中,在进行复习巩固以及深入学习iOS-底层原理 19:类扩展 与 关联对象 底层原理探索iOS 关联属性底层探索。

aa2002aa的博客 882

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

「OC」源码学习——objc_class的bits成员探究

可以看到,cache_t的内存为16字节,结合我们前面所计算的,那么我们不难得出。,但是上述这样做的结果会导致占用相当相当多的内存,因为据苹果官方统计只有大约10%的类需要真正地去修改它们的方法。在方法实现中来做区分,如果有rw_ext的类,其列表就错那个rw_ext中获得,如果没有,从ro中读取。,编译期生成的只读结构体,存储类的静态元数据,如方法列表、属性列表、协议列表、成员变量等。其实在寒假之前学过,我们可以通过分类的方法去重写原本类中的对象方法,那么由于。的方法列表指向的地址都指向了相同的位置。

aa2002aa的博客 1088

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

「OC」源码学习——属性关键字

其实属性就是成员变量和自动编写的存取方法。

aa2002aa的博客 473

【iOS】OC源码阅读——alloc源码分析

之前笔者简单学习了Objective-c,这段时间在阅读OC源码,通过阅读obj838来深入理解代码的运行机制、设计思想及实现细节。本篇博客主要分享一下我通过阅读和调试alloc部分的源码所学到的东西和遇到的问题。

2301_80467753的博客 1561

「OC」源码学习——alloc与init的实现

系统类(如NSObject因为NSObject在编译时已完成isa指针的初始化。所以不需要经过两次callAlloc只需要直接进入objc_alloc->callAlloc->直接申请空间即可第一次callAlloc通过objc_alloc进入,目的是检查类的初始化状态,类初次创建是没有默认的实现的所以继续向下执行进入到msgSend消息发送流程,为这个类进行初始化。第二次callAlloc,此时类已初始化完成,进入实际内存分配逻辑。callAlloc的分支逻辑// 直接分配内存。

aa2002aa的博客 934

「OC」源码学习——KVO底层原理探究

之前在学习回调传值的时候,就对KVO进行了学习,接下来我们来继续进一步学习KVO之中的相关内容Block 回调的调用位置1. 定义 KVO 核心类与模型@end@end@end@end@end// JCKVO.m#import。

aa2002aa的博客 1180

IOS 07 Swift调用OC源码及OC第三方框架

创建SwiftUseOCTest项目,是一个Swift语言的项目;再创建一个OC类文件,里面有静态方法,实例方法,字段等;然后在Swift项目中调用OC类;像OC类的复杂宏,在Swift类中是无法调用。

sziitjin的博客 1830

「OC」源码学习——cache_t的原理探究

上一次我们讲到了objc_class的isa和bits之中的内容,接下来就是cache_t之中的内容。

aa2002aa的博客 947

「OC」源码学习——对象的底层探索

上次我们说到了源码里面的调用顺序,现在我们继续了解我们上一篇文章没有讲完的关于对象的内容函数,完整了解对象的产生对于isa赋值以及内存申请的内容。

aa2002aa的博客 875

「OC」源码学习—— 消息发送、动态方法解析和消息转发

前面我们在学习alloc源码的时候,就在callAlloc源码之中简单的探究过,类初始化缓存的问题,我们知道在一个类第一次被实例化的时候,会调用去二次调用alloc方法,这篇文章就是探究我们在调用方法的时候,会需要经历一个什么流程。

aa2002aa的博客 1114

iOS 学习之旅 - OC 篇

我开始一边做 Android 的需求一边学习 OC 基础,一周后接了两个小需求(不是很难),我们主要是用的 OC 语言,很多同学新项目可能用的是 swift 我目前还没学,因为移动端大部分逻辑都在底层,由于之前写过一点点 C 和 C++ ,所以总的回顾起来压力不算太大。我们还需要了解编译与汇编的一些简单知识,这里我们只需要了解一些简单的指令就可以了,因为苹果只开源了部分代码,有些内容我们只能去看汇编,还有比如 OC 方法的执行过程中有部分代码就是汇编写的。关于 objc 的源码配置我这里提供一个。

z240336124的专栏 980

oc学习心得

oc学了将近1个月吧,临时断断续续学习的。因为我有其他语言的基础。学起来很快。 1.注意对objective-c基础的把握。其语法跟c语法很像。c语言的扩展。面向对象。Foundation框架封装了绝大多数类或者结构体。OC基础很重要。是学习UI的基础。 2.学了OC,不学UI,控件,相当于之前白学。因为就像你学了C或者C++却很难做出带页面的软件,还需要第三方库。 3.OC与UI交互,可以使用纯代码一个一个创建组件,也可以通过storyboard故事板拖控件实现,前者更灵活。后者开发速度快,也可以通过xi

A1521315qwss的博客 601
上一篇: 「OC」源码学习——KVO底层原理探究
下一篇: 「OC」初识runloop
小鹿撞出了脑震荡
博客等级 码龄3年 1107粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值