标签:man 获取 结构 tar nal 断点 ogr nonatomic dispatch
OC对象本质是C++的结构体:因为对象涉及到不同类型,只有结构体能存储不同的结构体
OC中的test方法会转化成c语言的方法
- (void)test {
}
其实是系统会传递两个参数过来
// self 方法调用者
// _cmd 方法名,等价于当前方法的selector,既@selector(test)
- (void)testId:(id)self _cmd:(SEL)_cmd {
}
//转化成CPP
void test(MJPerson* self, SEL _cmd) {
}
切换到文件目录,使用命令行
clang -rewrite-objc xxxx.m -o xxx.cpp
直接切换到iOS下64位系统可以使用的CPP代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxxx.m -o xxx.cpp
Core Foundation
硬性规定对象占用十六字节
一个NSObject对象占用多少内存?
系统分配了
16个字节
给NSObject
对象(通过malloc_size
函数获得)但
NSObject
对象内部只使用了8个字节
的空间(64bit
环境下,可以通过class_getInstanceSize
函数获得)
查看过程:
1. 将OC转换成C++代码,可以看到的NSObject的本质是一个结构体,其中包含了一个isa指针,占用了8个字节
// NSObject Implementation
struct NSObject_IMPL {
Class isa; // 8个字节
};
2. 查看源码
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
// Class‘s ivar size rounded up to a pointer-size boundary.
// 获取到类的实例对象的成员变量所占用的大小
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
查看alloc分配内存的过程
// 1.在源码中搜索allocWithZone,找到实现函数
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
#if __OBJC2__
// allocWithZone under __OBJC2__ ignores the zone parameter
(void)zone;
obj = class_createInstance(cls, 0);
#else
if (!zone) {
obj = class_createInstance(cls, 0);
}
else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
#endif
if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}
// 2. 查看class_createInstance函数
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class‘s info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
// 3. 查看cls->instanceSize(extraBytes);
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
可以看出Core Foundation框架要求对象至少占用16个字节
计算机中的正数用源码表示,负数用补码表示,而负数的补码是其反码+1 所以出现了-128
简单的说为了避免-0,负数的算法结果是每位都加了1
小端
模式,在内存中从高位向低位读取数据一个类的类对象(class对象)是唯一的,在内存中只会开辟一份存储空间
#import <Foundation/Foundation.h>
@interface NSObject (Test)
+ (void)test;
@end
//#import "NSObject+Test.h"
@implementation NSObject (Test)
//+ (void)test
//{
// NSLog(@"+[NSObject test] - %p", self);
//}
//
- (void)test
{
// self就是给谁发消息,不一定是当前类,谁接受消息,self就代表谁
NSLog(@"-[NSObject test] - %p", self);
}
@end
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
//+ (void)test
//{
// NSLog(@"+[MJPerson test] - %p", self);
//}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
// OC对象的调用方法,就是发送消息,发送过程中不会注意到是类方法还是对象方法
[MJPerson test];
// objc_msgSend([MJPerson class], @selector(test))
// isa -> superclass -> suerpclass -> superclass -> .... superclass == nil
[NSObject test];
// objc_msgSend([NSObject class], @selector(test))
/* 打印结果
[MJPerson class] - 0x1000011e0
[NSObject class] - 0x7fff8d775140
// self就是给谁发消息,不一定是当前类,谁接受消息,self就代表谁,所以这里的打印虽然发生在NSObject的分类中,但是其实消息是发送给MJPerson的,所以打印出的self是MJPerson的类对象
[NSObject test] - 0x1000011e0
[NSObject test] - 0x7fff8d775140
*/
}
return 0;
}
ISA_MASK
去源码里面获取
定义一个类MJPerson
,创建一个instance
对象和一个Class
对象,断点打印对应的isa
地址。
因为类对象的isa指针无法通过程序直接打印,可以创建一个结构体模仿类的实现,然后进行打印
struct mj_objc_class {
Class isa;
Class superclass;
};
// 使用
struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)([MJPerson class]);
从而得出
personClass->isa & ISA_MASK:0x00000001000014c8 = meta_class
未使用KVO监听的对象
使用KVO监听的对象
总结
:使用了KVO,会产生一个MJPerson
的子类(NSKVONotifying_MJPerson
),会改变MJPerson
的isa
指向新产生的子类,同时在新的子类里面会重写setAge:
方法,以及新增三个方法
NSKVONotifying_MJPerson | 类型 | 实现 |
---|---|---|
setAge: |
重写 | 实现Foundation 的_NSSetIntValueAndNotify 方法 |
class |
新增 | |
dealloc |
新增 | |
_isKVO |
新增 |
_NSSetIntValueAndNotify
内部实现
1. [self willChangeValueForKey:@"age"]
2. 原来的setter实现
3. [self didChangeValueForKey:@"age"]
didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法
_NSSet*ValueAndNotify
包括很多的基本数据类型
最后面参与编译的分类,方法的实现在最前面
使用
1. 通过Runtime加载某个类的所有Category数据
2. 把所有Category的方法、属性、协议数据,合并到一个大数组中
后面参与编译的Category数据,会在数组的前面
3. 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
分类的加载思路
使用msg_send
发送消息的调用顺序1.分类 2.父类
(比对子类和父类没有意义)
+load
方法会在runtime
加载类、分类写入内存
时调用- 每个类、分类的
+load
,在程序运行过程中只调用一次
第一级调用顺序 | 第二级调用顺序 |
---|---|
1.调用类的+load |
按照编译先后顺序调用(先编译,先调用) |
—— | 调用子类的+load 之前会先调用父类的+load |
2.调用分类的+load |
按照编译先后顺序调用(先编译,先调用) |
+initialize
方法会在类第一次接收到消息时调用
调用顺序
+initialize
,再调用子类的+initialize
+initialize
,就覆盖类本身的+initialize
调用 (父类的分类覆盖父类的,子类的分类覆盖子类的,相互之间不影响)+initialize
,多个子类只会调用一次父类的+initialize
。如果子类没有实现,那么调用子类的+initialize
会转化到调用父类的方法+initialize
和+load
的很大区别是,+initialize
是通过objc_msgSend
进行调用的,所以有以下特点
+initialize
,会调用父类的+initialize
+initialize
,就覆盖类本身的+initialize
调用+initialize
和+load
的区别
load方法
是在冷启动运行时阶段,类加载进内存时候调用,initialize
是在类第一次接收到消息时调用initialize
是通过objc_msgSend
方法进行调用的,所以会首先加载分类
的方法(依照objc_msgSend
调用规律),load
是直接找到方法地址
,通过指针
调取,会调用所有涉及到的load方法
(包括分类
的)+initialize
和+load
的联系
通常情况下两者都是只实现一次,不会重复实现。但是不排除重复实现的可能,譬如误操作直接调用这种,建议使用的时候加上dispatch_once
来使用
在类中声明一个属性,会做三件事情
1. 生成一个成员变量
2. 生成get和set方法的声明
3. 实现get和set方法的实现
在分类中写一个属性,只会自动生成get
和set
方法的声明
,不会生成方法实现,不会生成成员变量
默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但可以通过关联对象来间接实现
关联对象提供了以下API
1. 添加关联对象
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
2. 获得关联对象
id objc_getAssociatedObject(id object, const void * key)
3. 移除所有的关联对象
void objc_removeAssociatedObjects(id object)
static意味着只有当前文件内部可以访问
key的常见用法
objc_AssociationPolicy
objc_AssociationPolicy | 对应的修饰符 |
---|---|
OBJC_ASSOCIATION_ASSIGN |
assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC |
strong, nonatomic |
OBJC_ASSOCIATION_COPY_NONATOMIC |
copy, nonatomic |
OBJC_ASSOCIATION_RETAIN |
strong, atomic |
OBJC_ASSOCIATION_COPY |
copy, atomic |
关联对象原理(底层实现)
关联对象不会增加成员变量
,不能通过person -> age
形式进行访问
标签:man 获取 结构 tar nal 断点 ogr nonatomic dispatch
原文地址:https://www.cnblogs.com/CoderKJX/p/14329892.html