• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2023-04-28 09:45 Aet 隐藏边栏 |   抢沙发  13 
文章评分 2 次,平均分 5.0

创建对象

myClass.h

myClass.m

main

objc_object

消息

  1. OC中“函数调用的过程”就是“消息机制”, 简单说,OC中任何方法的调用,本质都是发送消息

    1. Objective-C的方法调用是通过消息传递机制实现的
    2. 但在底层,消息传递最终也会转化为函数调用,并在栈上执行,只是中间多了一层动态查找过程
  2. C++是直接函数调用(通过固定地址跳转)

  3. 具体消息见下文一些问题

id

  1. 此种类型变量可以存放任何数据类型的变量

继承

  1. oc不允许多继承关系

animal

cat

dog

main

alloc

  1. 为对象分配内存空间
    1. 类方法(由 NSObject 实现)
    2. 返回 未初始化 的对象
    3. 设置 retainCount = 1ARC 环境)
    4. C函数性能优化(不通过常规消息派发)
  2. 建立对象的基本元数据(特别是 isa 指针)
  3. 分配后内存布局:

init

  1. 子类可以重写init方法,但需要调用父类init
    1. 每个 init 方法必须先调用 [super init]
    2. NSObject 开始逐级初始化

dealloc

  1. 当引用计数归零时自动调用
  2. 绝对不要手动调用 [obj dealloc]

引用计数

  1. Objective-C Automatic Reference Counting修改为NO
  2. retain增加引用计数
  3. release减少引用计数
  4. 给对象发生release消息仅为放弃该对象的所有权,当引用计数为0时,真正销毁对象的是dealloc函数。所以如果需要在销毁前做一些其他事情,要重写dealloc函数

自动释放池

常量对象

  1. 常量对象没有引用计数,所以无法释放
  2. 常量对象的释放需要重写retainrelease【需要注意ARC

ARC

  1. ARCAutomatic Reference Counting ,自动引用计数)是一个编译期技术,利用此计数可以简化Objective-C在内存管理方面的工作量

使用权操作

所有权策略

  1. 创建对象时(alloc,init)/复制对象时(new copy mutableCopy)获得对象所有权
  2. 如果不使用alloc/init/new/copy/mutableCopy这些方法,或者不使用retain来保留一个对象,就不能成为对象的所有者

方法簇

  1. 一个方法要属于某个方法族,除了需要满足返回值和方法类别方面的要求之外,还需满足以下命名规则:
    1. 选择器同方法族名相同(开头的_可忽略)
    2. 或选择器的名字由方法族名加上非小写字母开头的字符串构成。

循环引用

弱引用

块对象

全局块

  1. 内存位置:编译后的数据区
  2. 生命周期:与程序相同
  3. 引用计数:不需要管理

栈块

  1. 内存位置:函数栈空间
  2. 生命周期:函数结束时自动销毁
  3. 致命风险:函数返回后使用导致崩溃

堆块

  1. 创建方式:显式调用 copy 或传递到需要长期持有的地方
  2. 内存位置:堆空间
  3. 生命周期:通过引用计数管理
  4. 必要操作:不再使用时必须 release(MRC) 或自动管理(ARC)

块的变量捕获机制

变量类型 捕获行为 内存影响 示例
局部变量 值拷贝 (原始类型) 创建独立副本 int a = 5; ^{ a; }
__block 变量 引用捕获 共享同一内存 __block int b = 10; ^{ b++; }
对象变量 强引用捕获 (默认) 可能引起循环引用 NSObject *obj; ^{ [obj method]; }
弱引用对象 __weak 修饰弱引用捕获 避免循环引用 __weak typeof(obj) weakObj = obj; ^{ [weakObj method]; }

Log

一些问题

消息机制

  1. 概念
    1. SEL(选择器):方法名的唯一标识,本质是char*字符串(类似"viewDidLoad")
    2. IMP(函数指针):指向实际函数实现的指针,类型为id (*IMP)(id, SEL, ...)
    3. 消息发送表达式会被编译器转化为:

  1. 消息传递流程
    1. 检查receiver是否为nil,若为nil则直接返回(无崩溃)
    2. 通过receiver->isa指针找到类对象
    3. 在类缓存中查找方法实现(快速查找)
      每个类有缓存(cache_t),使用SEL作为key查找缓存的IMP
    4. 若缓存未命中,则在类的方法列表中查找(慢速查找)
      遍历类的方法列表(包括父类),找到后存入缓存
    5. 若仍未找到,启动消息转发机制
      依次调用resolveInstanceMethod:forwardingTargetForSelector:methodSignatureForSelector:forwardInvocation

id为什么能容纳任意对象

  1. 底层定义
    1. id 是指向 objc_object 结构体的通用指针
    2. 任何 Objective-C 对象在内存中第一块数据必然是 isa 指针(占 8 字节)

  1. 核心机制:通用指针
    1. id 只关心对象头部的 isa 指针
    2. 不关心对象具体类型:编译器不做强类型检查
  2. 总结
    1. idisa位置,记录了所保存的数据的类型,但是不关心isa后面的数据内容
    2. 所以它可以存任何类型的数据,同时也可以根据isa的信息把id对象转换成所保证对象的真正类型

关于继承

  1. 类结构

  1. 消息查找流程
    1. 子类缓存
    2. 子类方法列表
    3. 继承链爬升
      父类缓存
      父类方法列表
    4. 消息转发三阶段
      当整条继承链都处理不了消息时

关于super

  1. 编译时转换
    1. 编译器指令
    2. 运行时定向查找

  1. objc_super

  1. 对比self
    1. super 直接在父类查找,跳过当前类(子类)的查找过程
    2. 实际调用的父类方法在编译时不确定
      实际调用取决于运行时 self 的父类
特性 [super method] [self method]
消息接收者 子类对象 子类对象
查找起点 直接从父类开始 从当前类(子类)开始
查找范围 父类及其继承链 子类→父类→NSObject
调用方式 objc_msgSendSuper() objc_msgSend()
典型用途 重写方法时调用父类实现 常规方法调用

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0 最后编辑于:2025-07-08
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享