深入剖析 OC 中的方法结构 (isa指针的指向,以及元类的概念等)

Objective-C底层原理:从isa指针到消息发送的完整解析 在面向对象编程中,理解类与对象的本质是掌握一门语言的核心。其原理通常围绕内存结构、方法调度和运行时动态性展开。以Objective-C为例,对象本质是指向objc_object结构体的指针,而类本身也是对象(元类),通过isa指针链构成其类型系统。这种设计实现了强大的动态特性,其核心价值在于支持灵活的消息发送机制(Message Passing)和运行时方法解析,为KVO、KVC、Category等高级特性提供了基础。在实际的iOS/macOS开发中,这一机制使得方法调用(如[obj message])能够 阅读详情



> 因为 ObjC 的 runtime 只能在 Mac OS 下才能编译,所以文章中的代码都是在 Mac OS,也就是 `x86_64` 架构下运行的,对于在 arm64 中运行的代码会特别说明。


在上一篇分析 `isa` 的文章[从 NSObject 的初始化了解 isa](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/objc/从%20NSObject%20的初始化了解%20isa.md) 中曾经说到过实例方法被调用时,会通过其持有 `isa` 指针寻找对应的类,然后在其中的 `class_data_bits_t` 中查找对应的方法,在这一篇文章中会介绍方法在 ObjC 中是如何存储方法的。


这篇文章的首先会根据 ObjC 源代码来分析方法在内存中的存储结构,然后在 lldb 调试器中一步一步验证分析的正确性。


## 方法在内存中的位置


先来了解一下 ObjC 中类的结构图:


<p align="center">

![objc-method-class](../images/objc-method-class.png)


+ `isa` 是指向元类的指针,不了解元类的可以看 [Classes and Metaclasses](http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)

+ `super_class` 指向当前类的父类

+ `cache` 用于缓存指针和 `vtable`,加速方法的调用

+ `bits` 就是存储类的方法、属性和遵循的协议等信息的地方


### `class_data_bits_t` 结构体


这一小结会分析类结构体中的 `class_data_bits_t bits`。


下面就是 ObjC 中 `class_data_bits_t` 的结构体,其中只含有一个 64 位的 `bits` 用于存储与类有关的信息:


<p align="center">

![objc-method-class-data-bits-t](../images/objc-method-class-data-bits-t.png)


在 `objc_class` 结构体中的注释写到 `class_data_bits_t` 相当于 `class_rw_t` 指针加上 rr/alloc 的标志。


```objectivec

class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

```


它为我们提供了便捷方法用于返回其中的 `class_rw_t *` 指针:


```objectivec

class_rw_t* data() {

   return (class_rw_t *)(bits & FAST_DATA_MASK);

}

```


将 `bits` 与 `FAST_DATA_MASK` 进行位运算,只取其中的 `[3, 47]` 位转换成 `class_rw_t *` 返回。


> 在 x86_64 架构上,Mac OS **只使用了其中的 47 位来为对象分配地址**。而且由于地址要按字节在内存中按字节对齐,所以掩码的后三位都是 0。


因为 `class_rw_t *` 指针只存于第 `[3, 47]` 位,所以可以使用最后三位来存储关于当前类的其他信息:


<p align="center">

![objc-method-class_data_bits_t](../images/objc-method-class_data_bits_t.png)



```objectivec

#define FAST_IS_SWIFT           (1UL<<0)

#define FAST_HAS_DEFAULT_RR     (1UL<<1)

#define FAST_REQUIRES_RAW_ISA   (1UL<<2)

#define FAST_DATA_MASK          0x00007ffffffffff8UL

```


+ `isSwift()`

    + `FAST_IS_SWIFT` 用于判断 Swift 类

+ `hasDefaultRR()`

    + `FAST_HAS_DEFAULT_RR` 当前类或者父类含有默认的 `retain/release/autorelease/retainCount/_tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference` 方法

+ `requiresRawIsa()`

    + `FAST_REQUIRES_RAW_ISA` 当前类的实例需要 raw `isa`


执行 `class_data_bits_t` 结构体中的 `data()` 方法或者调用 `objc_class` 中的 `data()` 方法会返回同一个 `class_rw_t *` 指针,因为 `objc_class` 中的方法只是对 `class_data_bits_t` 中对应方法的封装。


```objective

// objc_class 中的 data() 方法

class_data_bits_t bits;


class_rw_t *data() { 

   return bits.data();

}


// class_data_bits_t 中的 data() 方法

uintptr_t bits;


class_rw_t* data() {

   return (class_rw_t *)(bits & FAST_DATA_MASK);

}

```


### `class_rw_t` 和 `class_ro_t`


ObjC 类中的属性、方法还有遵循的协议等信息都保存在 `class_rw_t` 中:


```objectivec

struct class_rw_t {

    uint32_t flags;

    uint32_t version;


    const class_ro_t *ro;


    method_array_t methods;

    property_array_t properties;

    protocol_array_t protocols;


    Class firstSubclass;

    Class nextSiblingClass;

};

```


其中还有一个指向常量的指针 `ro`,其中存储了**当前类在编译期就已经确定的属性、方法以及遵循的协议**。


```objectivec

struct class_ro_t {

    uint32_t flags;

    uint32_t instanceStart;

    uint32_t instanceSize;

    uint32_t reserved;


    const uint8_t * ivarLayout;

    

    const char * name;

    method_list_t * baseMethodList;

    protocol_list_t * baseProtocols;

    const ivar_list_t * ivars;


    const uint8_t * weakIvarLayout;

    property_list_t *baseProperties;

};

```


**在编译期间**类的结构中的 `class_data_bits_t *data` 指向的是一个 `class_ro_t *` 指针:


<p align='center'>

![objc-method-before-realize](../images/objc-method-before-realize.png)



然后在加载 *ObjC 运行时*的时候调用 `realizeClass` 方法:


1. 从 `class_data_bits_t` 调用 `data` 方法,将结果从 `class_rw_t` 强制转换为 `class_ro_t` 指针

2. 初始化一个 `class_rw_t` 结构体

3. 设置结构体 `ro` 的值以及 `flag`

4. 最后设置正确的 `data`。


```objectivec

const class_ro_t *ro = (const class_ro_t *)cls->data();

class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);

rw->ro = ro;

rw->flags = RW_REALIZED|RW_REALIZING;

cls->setData(rw);

```


下图是 `realizeClass` 方法执行过后的类所占用内存的布局,你可以与上面调用方法前的内存布局对比以下,看有哪些更改:


<p align='center'>

![objc-method-after-realize-class](../images/objc-method-after-realize-class.png)


但是,在这段代码运行之后 `class_rw_t` 中的方法,属性以及协议列表均为空。这时需要 `realizeClass` 调用 `methodizeClass` 方法来**将类自己实现的方法(包括分类)、属性和遵循的协议加载到 `methods`、 `properties` 和 `protocols` 列表中**。


## XXObject


下面,我们将分析一个类 `XXObject` 在运行时初始化过程中内存的更改,这是 `XXObject` 的接口与实现:


```objectivec

// XXObject.h 文件

#import <Foundation/Foundation.h>


@interface XXObject : NSObject


- (void)hello;


@end


// XXObject.m 文件


#import "XXObject.h"


@implementation XXObject


- (void)hello {

    NSLog(@"Hello");

}


@end

```


> 这段代码是运行在 Mac OS X 10.11.3 (x86_64)版本中,而不是运行在 iPhone 模拟器或者真机上的,如果你在 iPhone 或者真机上运行,可能有一定差别。


<p align='center'>

![objc-method-target](../images/objc-method-target.png)


这是主程序的代码:


```objectvec

#import <Foundation/Foundation.h>

#import "XXObject.h"


int main(int argc, const char * argv[]) {

    @autoreleasepool {

        Class cls = [XXObject class];

        NSLog(@"%p", cls);

    }

    return 0;

}

```


### 编译后内存中类的结构


因为**类在内存中的位置是编译期就确定的**,先运行一次代码获取 `XXObject` 在内存中的地址。


```objectivec

0x100001168

```


接下来,在整个 ObjC 运行时初始化之前,也就是 `_objc_init` 方法中加入一个断点:


<p align="center">

![objc-method-after-compile](../images/objc-method-after-compile.png)


然后在 lldb 中输入以下命令:


```

(lldb) p (objc_class *)0x100001168

(objc_class *) $0 = 0x0000000100001168

(lldb) p (class_data_bits_t *)0x100001188

(class_data_bits_t *) $1 = 0x0000000100001188

(lldb) p $1->data()

warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.

(class_rw_t *) $2 = 0x00000001000010e8

(lldb) p (class_ro_t *)$2 // 将 class_rw_t 强制转化为 class_ro_t

(class_ro_t *) $3 = 0x00000001000010e8

(lldb) p *$3

(class_ro_t) $4 = {

  flags = 128

  instanceStart = 8

  instanceSize = 8

  reserved = 0

  ivarLayout = 0x0000000000000000 <no value available>

  name = 0x0000000100000f7a "XXObject"

  baseMethodList = 0x00000001000010c8

  baseProtocols = 0x0000000000000000

  ivars = 0x0000000000000000

  weakIvarLayout = 0x0000000000000000 <no value available>

  baseProperties = 0x0000000000000000

}

```


<p align="center">

![objc-method-lldb-print-before-realize](../images/objc-method-lldb-print-before-realize.png)


现在我们获取了类经过编译器处理后的只读属性 `class_ro_t`:


```objectivec

(class_ro_t) $4 = {

  flags = 128

  instanceStart = 8

  instanceSize = 8

  reserved = 0

  ivarLayout = 0x0000000000000000 <no value available>

  name = 0x0000000100000f7a "XXObject"

  baseMethodList = 0x00000001000010c8

  baseProtocols = 0x0000000000000000

  ivars = 0x0000000000000000

  weakIvarLayout = 0x0000000000000000 <no value available>

  baseProperties = 0x0000000000000000

}

```


可以看到这里面只有 `baseMethodList` 和 `name` 是有值的,其它的 `ivarLayout`、 `baseProtocols`、 `ivars`、`weakIvarLayout` 和 `baseProperties` 都指向了空指针,因为类中没有实例变量,协议以及属性。所以这里的结构体符合我们的预期。


通过下面的命令查看 `baseMethodList` 中的内容:


```objectivec

(lldb) p $4.baseMethodList

(method_list_t *) $5 = 0x00000001000010c8

(lldb) p $5->get(0)

(method_t) $6 = {

  name = "hello"

  types = 0x0000000100000fa4 "v16@0:8"

  imp = 0x0000000100000e90 (method`-[XXObject hello] at XXObject.m:13)

}

(lldb) p $5->get(1)

Assertion failed: (i < count), function get, file /Users/apple/Desktop/objc-runtime/runtime/objc-runtime-new.h, line 110.

error: Execution was interrupted, reason: signal SIGABRT.

The process has been returned to the state before expression evaluation.

(lldb)

```


<p align="center">

![objc-method-lldb-print-method-list](../images/objc-method-lldb-print-method-list.png)


使用 `$5->get(0)` 时,成功获取到了 `-[XXObject hello]` 方法的结构体 `method_t`。而尝试获取下一个方法时,断言提示我们当前类只有一个方法。


### realizeClass


这篇文章中不会对 `realizeClass` 进行详细的分析,该方法的主要作用是对类进行第一次初始化,其中包括:


+ 分配可读写数据空间

+ 返回真正的类结构


```objectivec

static Class realizeClass(Class cls)

```


上面就是这个方法的签名,我们需要在这个方法中打一个条件断点,来判断当前类是否为 `XXObject`:


<p align="center">

![objc-method-lldb-breakpoint](../images/objc-method-lldb-breakpoint.png)


这里直接判断两个指针是否相等,而不使用 `[NSStringFromClass(cls) isEqualToString:@"XXObject"]` 是因为在这个时间点,这些方法都不能调用,在 ObjC 中没有这些方法,所以只能通过判断类指针是否相等的方式来确认当前类是 `XXObject`。


> 直接与指针比较是因为类在内存中的位置是编译期确定的,只要代码不改变,类在内存中的位置就会不变(已经说过很多遍了)。


<p align="center">

![objc-method-breakpoint-before-set-r](../images/objc-method-breakpoint-before-set-rw.png)



这个断点就设置在这里,因为 `XXObject` 是一个正常的类,所以会走 `else` 分支为**可写的类数据**分配内存。


> 运行代码时,因为每次都会判断当前类指针是不是指向的 `XXObject`,所以会等一会才会进入断点。


在这时打印类结构体中的 `data` 的值,发现其中的布局依旧是这样的:


<p align="center">

![objc-method-before-realize](../images/objc-method-before-realize.png)



在运行完这段代码之后:


<p align="center">

![objc-method-after-realize-breakpoint](../images/objc-method-after-realize-breakpoint.png)


我们再来打印类的结构:


```objectivec

(lldb) p (objc_class *)cls // 打印类指针

(objc_class *) $262 = 0x0000000100001168

(lldb) p (class_data_bits_t *)0x0000000100001188 // 在类指针上加 32 的 offset 打印 class_data_bits_t 指针

(class_data_bits_t *) $263 = 0x0000000100001188

(lldb) p *$263 // 访问 class_data_bits_t 指针的内容

(class_data_bits_t) $264 = (bits = 4302315312)

(lldb) p $264.data() // 获取 class_rw_t

(class_rw_t *) $265 = 0x0000000100701f30

(lldb) p *$265 // 访问 class_rw_t 指针的内容,发现它的 ro 已经设置好了

(class_rw_t) $266 = {

  flags = 2148007936

  version = 0

  ro = 0x00000001000010e8

  methods = {

    list_array_tt<method_t, method_list_t> = {

       = {

        list = 0x0000000000000000

        arrayAndFlag = 0

      }

    }

  }

  properties = {

    list_array_tt<property_t, property_list_t> = {

       = {

        list = 0x0000000000000000

        arrayAndFlag = 0

      }

    }

  }

  protocols = {

    list_array_tt<unsigned long, protocol_list_t> = {

       = {

        list = 0x0000000000000000

        arrayAndFlag = 0

      }

    }

  }

  firstSubclass = nil

  nextSiblingClass = nil

  demangledName = 0x0000000000000000 <no value available>

}

(lldb) p $266.ro // 获取 class_ro_t 指针

(const class_ro_t *) $267 = 0x00000001000010e8

(lldb) p *$267 // 访问 class_ro_t 指针的内容

(const class_ro_t) $268 = {

  flags = 128

  instanceStart = 8

  instanceSize = 8

  reserved = 0

  ivarLayout = 0x0000000000000000 <no value available>

  name = 0x0000000100000f7a "XXObject"

  baseMethodList = 0x00000001000010c8

  baseProtocols = 0x0000000000000000

  ivars = 0x0000000000000000

  weakIvarLayout = 0x0000000000000000 <no value available>

  baseProperties = 0x0000000000000000

}

(lldb) p $268.baseMethodList // 获取基本方法列表

(method_list_t *const) $269 = 0x00000001000010c8

(lldb) p $269->get(0) // 访问第一个方法

(method_t) $270 = {

  name = "hello"

  types = 0x0000000100000fa4 "v16@0:8"

  imp = 0x0000000100000e90 (method`-[XXObject hello] at XXObject.m:13)

}

(lldb) p $269->get(1) // 尝试访问第二个方法,越界

error: Execution was interrupted, reason: signal SIGABRT.

The process has been returned to the state before expression evaluation.

Assertion failed: (i < count), function get, file /Users/apple/Desktop/objc-runtime/runtime/objc-runtime-new.h, line 110.

(lldb)

```


<p align="center">

![objc-method-print-class-struct-after-realize](../images/objc-method-print-class-struct-after-realize.png)


> 最后一个操作实在是截取不到了


```objectivec

const class_ro_t *ro = (const class_ro_t *)cls->data();

class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);

rw->ro = ro;

rw->flags = RW_REALIZED|RW_REALIZING;

cls->setData(rw);

```


在上述的代码运行之后,类的只读指针 `class_ro_t` 以及可读写指针 `class_rw_t` 都被正确的设置了。但是到这里,其 `class_rw_t` 部分的方法等成员的指针 `methods`、 `protocols` 和 `properties` 均为空,这些会在 `methodizeClass` 中进行设置:


<p align="center">

![objc-method-after-methodizeClass](../images/objc-method-after-methodizeClass.png)


在这里调用了 `method_array_t` 的 `attachLists` 方法,将 `baseMethods` 中的方法添加到 `methods` 数组之后。我们访问 `methods` 才会获取当前类的实例方法。


## 方法的结构


说了这么多,到现在我们可以简单看一下方法的结构,与类和对象一样,方法在内存中也是一个结构体。


```objectivec

struct method_t {

    SEL name;

    const char *types;

    IMP imp;

};

```


其中包含方法名,类型还有方法的实现指针 `IMP`:


<p align="center">

![obj-method-struct](../images/obj-method-struct.png)


上面的 `-[XXObject hello]` 方法的结构体是这样的:


```objectivec

name = "hello"

types = 0x0000000100000fa4 "v16@0:8"

imp = 0x0000000100000e90 (method`-[XXObject hello] at XXObject.m:13

```


方法的名字在这里没有什么好说的。其中,方法的类型是一个非常奇怪的字符串 `"v16@0:8"` 这在 ObjC 中叫做*类型编码*(Type Encoding),你可以看这篇[官方文档](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)了解与类型编码相关的信息。


对于方法的实现,lldb 为我们标注了方法在文件中实现的位置。


## 小结


在分析方法在内存中的位置时,笔者最开始一直在尝试寻找**只读**结构体 `class_ro_t` 中 `baseMethods` 第一次设置的位置(了解类的方法是如何被加载的)。尝试从 `methodizeClass` 方法一直向上找,直到 `_obj_init` 方法也没有找到设置只读区域的 `baseMethods` 的方法。


而且在 runtime 初始化之后,`realizeClass` 之前,从 `class_data_bits_t` 结构体中获取的 `class_rw_t` 一直都是错误的,这个问题在最开始非常让我困惑,直到后来在 `realizeClass` 中发现原来在这时并不是 `class_rw_t` 结构体,而是`class_ro_t`,才明白错误的原因。


后来突然想到类的一些方法、属性和协议实在编译期决定的(`baseMethods` 等成员以及类在内存中的位置都是编译期决定的),才感觉到豁然开朗。


1. 类在内存中的位置是在编译期间决定的,在之后修改代码,也不会改变内存中的位置。

2. 类的方法、属性以及协议在编译期间存放到了“错误”的位置,直到 `realizeClass` 执行之后,才放到了 `class_rw_t` 指向的只读区域 `class_ro_t`,这样我们即可以在运行时为 `class_rw_t` 添加方法,也不会影响类的只读结构。

3. 在 `class_ro_t` 中的属性在运行期间就不能改变了,再添加方法时,会修改 `class_rw_t` 中的 `methods` 列表,而不是 `class_ro_t` 中的 `baseMethods`,对于方法的添加会在之后的文章中分析。


## 参考资料


+ [Classes and Metaclasses](http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html)

+ [Tagged Pointer](https://en.wikipedia.org/wiki/Tagged_pointer)

+ [类型编码](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)

+ [Type Encodings](http://nshipster.cn/type-encodings/)


Follow: [@Draveness](https://github.com/Draveness)


isa与类关联的原理 alloc & init & new 源码分析文章中,alloc有3个核心操作,其中一个就是obj->initInstanceIsa,即类与isa绑定。主要理解类与isa是如何关联的。 objc4中分析obj->initInstanceIsa源码 找到obj->initInstanceIsa源码 首先从alloc进入objc的源码,找到obj = (id)calloc(1, size);操作,涉及的方法顺序是alloc --> _objc_rootAlloc --& 阅读详情

相关推荐

Java后端实战:钉钉互动卡片消息从配置到生产级对接

在企业级消息推送场景中,机器人通知已从简单的文本向富交互演进。其核心原理在于通过可交互的卡片模板,将传统单向通知升级为双向、有状态的微型应用界面,实现了用户无需跳转即可完成操作的技术价值。这广泛应用于审批、任务确认、数据填报等需要即时反馈的业务流程。本文聚焦于钉钉平台,深入剖析其互动卡片消息机制,并基于Java后端,系统讲解如何实现包括安全验签、异步回调与卡片动态更新在内的生产级对接方案,涵盖配置管理、服务降级等工程实践关键点,助力构建稳定高效的企业消息交互服务。

weixin_30617737的博客 435

通过Runtime源码了解Objective-C中的方法存储

原文链接 有经验的iOS开发者应该都知道,Objective-C是动态语言,Objective-C中的方法调用严格来说其实是消息传递。举例来说,调用对象A的hello方法 [A hello]; 复制代码其实是向A对象发送了@selector(hello)消息。 在上一篇文章Runtime中的isa结构体中提到过,对象的方法是存储在类结构中的,之所以这样设计是出于内存方面的考虑。那么,方法是如何在类...

weixin_33705053的博客 199

大白话讲解Numpy中什么是一维、二维、三维数组,以及5种创建方式

NumPy中提供了多种创建数组的方式,其中最基本的方式就是通过array()函数创建数组在使用该函数时直接传入列表或元组即可。 通过array()函数分别创建一维数组、二维组和三维数组,那么什么是维度呢?一维、二维、三维有什么区别? 1、什么是维度? 维度可以理解为描述数据的一种 “方向” 或者 “层次”。在数组中,维度的数量决定了我们需要用多少个索引来访问一个元素 2、一维、二维、三维的区别? (1)一维:一维数组就像是一条直线上排列的一组数据,例如: a = np.array([1, 2

1361

warning: could not execute support code to read Objective-C class data in the process.

iOS开发做压力测试的过程中出现: warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available. 出现这个问题的时候我使用的是前段时间介绍异步线程改同步调用的方法...

longlongValue 2万+

【大模型推理】工作负载的弹性伸缩

推荐采用 并发请求数(Concurrency) 或 队列深度(Queue Size) 结合GPU监控指标(显存/算力利用率),并依托Knative KPA、Fluid缓存、优先级调度等技术实现高效弹性伸缩[citation:6][citation:7][citation:8]。QPS无法体现这种资源消耗累积效应[citation:6]。并发数的本质:是实时反映资源占用的瞬时状态量,而非速率指标,尤其适合单次请求资源消耗大、处理时间长的业务(如LLM推理)[citation:1][citation:6]。

王尚权 qq:2515162716 1183

国产安全芯片在密码算法国密体系下的性能适配策略

国密体系即国家商用密码体系,是保障信息安全的核心技术体系。自20世纪90年代末起步,国密体系经历了从无到有、从弱到强的发展历程。1999年,国家密码管理局成立,标志着国密体系的正式建立。2000年,国密SM1算法首次发布,开启了国密算法的标准化进程。此后,SM2、SM3、SM4等算法陆续推出,形成了完整的对称与非对称密码算法体系。:2010年,国密标准正式纳入国家标准体系,国密算法在金融、政务、通信等关键领域的应用得到明确规范。

qq_40552871的博客 1354

This may reduce the quality of type information available.

warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available. 方法自己调用了自己: set_Method:(NSString *)text { [self ...

chqj_163的专栏 1012

第1关:STL模板之queue实例一:最少个数

头歌自创答案

xiyunSacire的博客 817

[Error]Swift开发调试时使用LLDB的po和print命令无法输出变量

2. 使用frame variable -L veriablename打印堆栈地址。用Swift开发时,使用LLDB的po和print命令在控制台输出变量报错。3. 使用frame variable -o veriablename打印。1. 使用frame variable variablename打印。frame variable可以简写成fr v或v。

Gamin 3092

从零构建个性化AI助手:基于Mem0与本地大模型的记忆化实践

本文详细介绍了如何利用Mem0与本地大模型构建个性化AI助手,实现记忆化应用结构的实践方法。从开发环境搭建、记忆存储机制到个性化交互实现,提供了完整的解决方案和代码示例,帮助开发者在本地环境中创建能记住用户习惯和偏好的智能助手。

weixin_29214335的博客 464

MCU 微控制器,在实际项目中如何选型(以 STM32为例)

1.MCU 选型需要考虑的一些因素 2.案例:传感器设备的 MCU 选型

创享 AIOT 的专栏 1万+

iOS 15 oc语言适配

1、首先升级一下xcode。不然一些方法调用不起来。 2、 self.navigationController.navigationBar.barTintColor = [UIColor clearColor]; 在iOS 15上设置失效。需要采用 if (@available(iOS 15.0, *)) { UINavigationBarAppearance *barApp = [UINavigationBarAppearance new]; barApp.backgroundColor = [UI

sinat_27908353的博客 471

infercnv的实操

摘要: 博主在infercnv分析中遇到执行困难,外包服务不可靠(未按要求使用HMM方法)。通过优化策略(逐样本处理、精简细胞至1000个、固定随机种子)解决了运行问题,但HMM功能仍无法实现。最终参考Cell期刊标准设置阈值完成分析,后续将继续更新进展。该案例凸显了生物信息分析中自主优化的重要性。(150字)

xiaoyusang的博客 306

OC类与对象上半章节较为重要的部分

分享会的时候对于isKindOfClass和isMemberOfClass两者的区别不了解,所以写了这篇博客来记录上次的遗漏点。

qq_73106050的博客 941

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Objective-C学习 实例对象,类对象, 元类对象与 isa指针

继承自NSObject 的对象结构大致是, 这是一个继承自NSobject 的对象的Student, (被添加了name 和 age 两个属性)实例对象就是通过类 alloc 出来的对象 , 每一次调用 alloc 都会产生一个新的instance对象。所有的OC对象都继承自NSObject, 类主要是基于结构体去实现的。通过实例对象的class方法或类的class方法获取到类对象,指针,它指向该对象所属的类,是 OC 消息发送机制的核心。oc的对象, 类, 主要是基于结构体去实现的。

for_ever_love__的博客 376

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

RunTime源码中的基本结构体【类,对象,isa】

文章目录关于我的仓库前言准备工作类与对象对象objc_object补充知识:OC类的本质找到它?isa_t类objc_class说明:isa指针引入概念:元类与根类isa闭环补充知识:类方法与实例方法的存储isa详解完整定义uintptr_t bits【unsigned long bits】union isa_t结构分析补充知识:arm 64与x86_64各个部分存储空间isa_t的初始化init...

KevinAshen的博客 415

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Runtime源码剖析---图解对象、类与isa

文章目录iOS开发---图解对象、类与isa对象objc_object定义如何在源码中找到它?isa_t类objc_class定义成员变量继承关系元类isa_t结构剖析结构分析cls对象bits对象structisa_t初始化过程`indexed` 和 `magic``has_cxx_dtor``shiftcls`其他位域isa的应用获取cls地址class方法isMemberOfClass&am...

qq_42376419的博客 705

[iOS]浅析isa指针

[iOS]浅析isa指针

m0_74703932的博客 1322

Runtime之对象、类(类对象)、元类

在C++/Java/C#/OC等众多面向对象语言,它们都有着面相对象一些共有的特性,但是也都有各自的一些区别。今天我们就从运行时底层来剖析一下OC的特性。 先说说OC中的类 Person *person = [Person new]; Dog *dog = [Dog new]; Class class1 = [person class]; Class class2 = [do

每天进步一点点 3327

iOS底层原理--isa&类结构探究

特殊之处在根元类(NSObject)最后又继承了根类(NSObject) 子元类---->父元类---->父元类的父元类---->根元类(NSObject)---->根类(NSObject)---->nil。如果没有,则是成员变量。类的实例方法存储在bits-->data()-->methods()-->list,其中既包括实例方法,还包括属性的set/get方法。的打印信息,我们可以看出类的继承关系:子类---->父类---->父类的父类---->根类(NSObject)---->nil。

yinshipin007的博客 494

iOS类的本质与底层探索

前言 首先,我们看下几个类型:NSObject、Class、objc_object、objc_class、id的联系与区别。 NSObject:OC中的基类,绝大多数类都继承NSObject(NSProxy也是基类哦~) Class:NSObject的类型,在objc源码的NSObject.mm文件中可以看到 + (Class)class { return self; } objc_object:NSObject类在C++的底层实现的结构体名称,在生成的cpp文件中可以看到,它和N

wywinstonwy的专栏 3810

C语言剖析OC的rangeOfString方法

在OC中,我们选择- (NSRange)rangeOfString:方法来判断字符串是否存在。该方法返回的是一个NSRange 类型的结构体,成员变量包括location、length。 需要注意的是:当这个字符串不存在时,返回的location不仅仅是NSNotFound,并且length的值也是0.

筱锐 2408

ObjC中的类和实例对象

NSObject OC中类的本质是一个结构体 NSObject类中存在一个Class类型的isa指针。 我们在Xcode编写一个类继承于NSObject,在terminal使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xx.m -o xx.cpp 将.m文件转成.cpp文件,窥探NSObject的底层实现。 我们发现NSObj...

weixin_34174422的博客 300

KVO 底层原理深度解析:为什么监听后对象的 isa 会变?

KVO 实现机制分析:当添加 KVO 监听时,Runtime 会动态生成名为 NSKVONotifying_ClassName 的子类,并将被观察对象的 isa 指针指向该子类。这个子类重写了原类的 setter 方法,在属性修改前后插入通知逻辑。通过改变 isa 指针,KVO 实现了对属性变化的自动监听,同时保持对原类的透明性。移除观察者后,isa 指针会恢复指向原类。

Sheffi 1057

【iOS】源码学习-类与对象底层原理

这里简单了解下类结构,iOS类的结构分析笔者将单独写一篇博客总结。

2401_87841908的博客 570

oc中对指针学习记录

oc 对象指针解惑

zjy_hll的专栏 459
上一篇: 你所不了解 load 方法
下一篇: 神秘的 isa 指针
appdeveloperxg
博客等级 码龄11年 38粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值