1.分类Category的使用

// 给MJPerson类添加分类
@interface MJPerson : NSObject
- (void)run;
@end@implementation MJPerson
- (void)abc{}
- (void)run{NSLog(@"MJPerson - run");}
+ (void)run2{}
@end// MJPerson的分类,给MJPerson添加test方法
@interface MJPerson (Test)
- (void)test;
@end @implementation MJPerson (Test)
- (void)run{ NSLog(@"MJPerson (Test) - run");}
- (void)test{ NSLog(@"test");} // 会存放在类对象中
+ (void)test2 {} // 会存放在元类对象中
@end// 调用拓展出来的方法
MJPerson *person = [[MJPerson alloc] init];
[person run];
[person test];

2.Category的本质

  • 编译时会把Category数据(实例|类方法、属性、协议)放到一个category_t的结构体内。
  • 在程序运行的时候,通过Runtime将Category的数据合并到类信息(类对象、元类对象)
  • 细节:运行时候,通过Runtime加载某个类的所有Category数据,把所有Category数据,合并到一个数组中(后面参与编译的Category数据,会放在数组的前面)。然后将合并后的分类数据,插入到类原来类数据的前面(所以当category和类的方法重名的时候,会调用Category方法)。

Category与extension本质的区别

  • Extension在编译的时候,它的数据包含在类信息中。
  • Category是在运行的时候,才会将数据合并到类信息中。

+load方法(+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用)

+load方法会在runtime加载类、分类时调用。每个类、分类的+load,在程序运行过程中只调用一次。其调用顺序如下:

  • <1>.先调用类的+load,按照编译先后顺序调用(先编译,先调用,调用子类的+load之前会先调用父类的+load)
  • <2>.再调用分类的+load,按照编译先后顺序调用(先编译,先调用)

注意:主动调用load方法,会和消息发送机制一样,会调到分类里面的load方法。

+Initialize方法

  1. +initialize方法会在类第一次接收到消息时调用,调用顺序:先调用父类的+initialize,再调用子类的+initialize (先初始化父类,再初始化子类,每个类只会初始化1次)
  2. +initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,是在类第一次接收到消息的时候调用。而load是根据函数地址直接调用,实在runtime加载类、分类的时候调用,且只调用一次。
  • 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次;调用子类,必先调用父类+initialize(第一次),然后调用这个子类的+initialize,但是这个子类没有实现,那么调用父类+initialize(第二次)方法。通过isa、supperclass指针找方法来调用)
  • 如果分类实现了+initialize,就覆盖类本身的+initialize调用。

2.分类添加成员变量(关联对象)

在分类中添加一个属性,只会生成声明,不会生成成员变量和set/get的实现。

@interface MJPerson (Test)
// 各类中添加属性
@property (assign, nonatomic) int weight;
// 只会生成方法的声明
//- (void)setWeight:(int)weight;
//- (int)weight;
@end// 实现get set方法 来使得分类中的方法可以赋值和访问
#define MJKey [NSString stringWithFormat:@"%p", self]
@implementation MJPerson (Test)NSMutableDictionary *names_;
NSMutableDictionary *weights_;
+ (void)load
{weights_ = [NSMutableDictionary dictionary];names_ = [NSMutableDictionary dictionary];
}- (void)setName:(NSString *)name { names_[MJKey] = name; }
- (NSString *)name { return names_[MJKey]; }- (void)setWeight:(int)weight { weights_[MJKey] = @(weight); }- (int)weight{ return [weights_[MJKey] intValue]; }

关联对象

#import "MJPerson.h"
@interface MJPerson (Test)
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) int weight;
@end#import "MJPerson+Test.h"
#import <objc/runtime.h>@implementation MJPerson (Test)
- (void)setName:(NSString *)name
{objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}- (NSString *)name
{// 隐式参数// _cmd == @selector(name)   只有get方法才可以这么写return objc_getAssociatedObject(self, _cmd);
}- (void)setWeight:(int)weight
{objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (int)weight
{// _cmd == @selector(weight)return [objc_getAssociatedObject(self, _cmd) intValue];
}// 这样就可以像调用类属性一样来调用分类里面的属性
person.name = "Lewis"

关联对象本质

实现关联对象技术的核心如下:

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

3.block

block的定义、简单使用

// 格式
返回值 (^block)(参数) = ^{ block的内容 }// 例子
void (^block)(void) = ^{ NSLog(@"this is a block") }
// 调用
block()

block本质

  • block本质上也是一个OC对象(最终继承于NSBlock->NSObject),它内部也有个isa指针,而且有函数调用地址,和捕获的变量。
  • block是封装了函数调用以及函数调用环境的OC对象。
  • block的底层结构如下图所示。

block捕获变量

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制。

<1>. 捕获局部auto类型变量(自动变量:离开作用域就会自动销毁)
<2>. 也可以捕获局部static类型变量
// 默认定义的局部变量,默认就是auto
// auto int age = 10
int age = 10    // 传递是age的值
static int height = 10   // 传递的是height的地址值
// 在创建这个block对象的时候,age和height的值已经存储到了block的内存中
void (^block)(void) = ^{// age和height的值已经被捕获进来了NSLog(@"age is %d, height is %d", age, height);
};
age = 20;
height = 20;
// 通过*height取出height的值,只是通过指针取出指向的外部的值
block(); // 输出:age is 10, height is 20

注:1>.self(局部变量)也会被捕获。2>.如果block里面使用的成员变量,那么其实捕获的是self对象。

block的类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。

  1. __NSGlobalBlock__ ( _NSConcreteGlobalBlock )
  2. __NSStackBlock__ ( _NSConcreteStackBlock )
  3. __NSMallocBlock__ ( _NSConcreteMallocBlock )

存放区域如下:

  • .text区域:主要放程序的代码
  • .data区域:数据段一般放一些全局变量
  • 堆:一般放那些alloc出来的数据,动态分配内存的。特点是需要开发者写代码申请的。需要开发者管理内存。
  • 栈:放一些局部变量。特点是系统自动分配内存。
int a = 10;// 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
void (^block1)(void) = ^{NSLog(@"Hello");
};
int age = 10;
void (^block2)(void) = ^{NSLog(@"Hello - %d", age);
};NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{NSLog(@"%d", age);
} class]);// 输出:__NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__

void (^block)(void);
void test2()
{// NSStackBlockint age = 10;block = [^{NSLog(@"block---------%d", age);} copy];// copy操作之后 block就会在堆上,就会变成NSMallocBlock// 如果没有copy操作,那么这个block捕获的是auto变量,所以是一个NSStackBlock,所以在作用域之外调用,打印出的age值不会是10,因为这个age已被释放[block release];
}void test()
{// Global:没有访问auto变量void (^block1)(void) = ^{NSLog(@"block1---------");};// Stack:访问了auto变量int age = 10;void (^block2)(void) = ^{NSLog(@"block2---------%d", age);};NSLog(@"%p", [block2 copy]);//    NSLog(@"%@ %@", [block1 class], [block2 class]);
}int age = 10;int main(int argc, const char * argv[]) {@autoreleasepool {int a = 10;NSLog(@"数据段:age %p", &age);NSLog(@"栈:a %p", &a);NSLog(@"堆:obj %p", [[NSObject alloc] init]);NSLog(@"数据段:class %p", [MJPerson class]);}return 0;
}

block的自动copy操作

当在ARC机制下有下列情况的时候,会自动对block进行copy操作

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时

ARC下block属性的建议写法

  • @property (strong, nonatomic) void (^block)(void);
  • @property (copy, nonatomic) void (^block)(void);

对象类型的auto变量、__block变量、__block修饰的对象

  • 当block内部访问了对象类型的auto变量时:如果block是在栈上,将不会对auto变量、__block变量和__block修饰的对象产生强引用。
  • 如果block被拷贝到堆上,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。(而__block修饰的auto变量(如int类型),则形成强引用,无弱引用
  • 如果block从堆上移除,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的auto变量(release)

而__block修饰的对象会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)

__block修饰符

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量、静态变量(static)
  • 编译器会将__block变量包装成一个对象

循环引用的解决

oc 协议 回调 静态成员_OC底层原理探究:Category、关联对象和block本质相关推荐

  1. oc 协议 回调 静态成员_OC问题

    一.属性readwrite,readonly,assign,retain,copy,nonatomic各是什么作用,在那种情况下用? 1. readwrite是可读可写特性;需要生成getter方法和 ...

  2. oc 协议 回调 静态成员_OC中特性、静态成员(static)以及协议的基本知识

    特性:@property  @synthesize @property是一种新的编译器功能,表示声明了一个新对象的属性:存在于接口部分(interface). 例如:@property NSStrin ...

  3. oc 协议 回调 静态成员_深入iOS系统底层之静态库

    少长咸集,群贤毕至.--<王羲之・兰亭集序> 目标文件 目标文件结构 程序员编写的是源代码,而计算机运行的则是CPU能识别的机器指令,因此必须要有一系列工具或程序来将源代码转化为机器指令, ...

  4. oc 协议 回调 静态成员_每日一问:c++类的成员函数,能作为线程的参数吗?

    问:类的成员函数可以传入线程参数吗? 回答: 如果c语言的全局函数,可以. 如果是类的静态成员函数,可以 如果是类的普通成员函数,不可以 为什么? <深入探索C++对象模型>中提到成员函数 ...

  5. oc 协议 回调 静态成员_ios – 在一个块中,__block变量和静态变量之间的实际区别是什么?...

    出于本答案的目的,假设两个示例都包含在 – (void)useGadgetsOnWidgets {-}中. 假设ARC,您的应用程序是单线程的,并且代码是不可重入的(即useGadgetsOnWidg ...

  6. KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听

    书读百变,其义自见! 将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :GitHub   -链接如果失效请自动搜索:https://github.com/henusjj/KVO_base 代码中有详 ...

  7. 网络协议从入门到底层原理(11)网络爬虫、无线网络、HTTP缓存、即时通信、流媒体

    补充知识 网络爬虫 网络爬虫的简易实例 robots.txt 无线网络 HTTP 缓存(Cache) 缓存 - 响应头 缓存 - 请求头 缓存的使用流程 即时通信(IM) XMPP MQTT 流媒体 ...

  8. 网络协议从入门到底层原理(10)WebSocket、WebService、RESTful、HTTPDNS、FTP文件传输协议、邮件相关协议、IPv6

    其他协议 WebSocket WebSocket - 建立连接 WebService RESTful HTTPDNS FTP文件传输协议 邮件相关的协议(SMTP.POP.IMAP) POP vs I ...

  9. 网络协议从入门到底层原理(9)HTTP/1.1的升级改进(HTTP/2、HTTP/3)

    HTTP的升级改进 HTTP/1.1协议的不足 SPDY 协议 HTTP/2 HTTP/2的特性 - 二进制格式 HTTP/2基本概念 - 数据流.消息.帧 HTTP/2的特性 - 多路复用(Mult ...

最新文章

  1. Java 8中一些常用的全新的函数式接口
  2. php批量请求url_php请求url的方法小结
  3. java agv,Java 访问控制关键字
  4. bio和bieos哪个标注模式好_阿里巴巴和亚马逊电商模式差异?哪个电商好做
  5. AC自动机:例题与机制详解
  6. [转]ABAP动态取得数据
  7. eclipse maven plugin 插件安装和配置
  8. C语言链表与malloc函数
  9. 用过滤器来解决JSP中文乱码问题
  10. 计算机管理有U盘 为啥不显示,U盘插入电脑后不显示怎么办?
  11. “双减”背景下初中数学差异化作业设计研究——海门区“十四五”规划课题开题报告
  12. Word背景变成豆绿色怎么恢复?
  13. 目录_Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)
  14. 跑语义分割程序时报错
  15. 分形理论在地理信息科学研究中的应用
  16. matlab horn antenna,antennas 天线阵列设计的matlab源码,非常有用 238万源代码下载- www.pudn.com...
  17. NMOS和PMOS管 电流方向和应用电路
  18. 数学在计算机科学中的作用,计算机科学中数学的重要性及其运用
  19. 01 Java体系
  20. 被win10的诸多功能无法使用折腾个半死

热门文章

  1. git 关于commit命令的修改
  2. 运行在CentOS7.5上的Django项目时间不正确问题
  3. 2022-2028年中国SUV市场投资分析及前景预测报告
  4. Windows Python3.6 安装 IPython(Jupyter) qtconsole
  5. 使用maven搭建ssm框架的javaweb项目
  6. oracle 11g完全安装教程(CentOS)
  7. 写给自己的web开发资源
  8. JSon数据查询---Jlinq
  9. [Ubuntu] 安装/卸载 声卡驱动
  10. usaco The Castle