iOS Object-C中类对象的本质

该内容仅供自己学习记录,前前后后也积累了好多有关Objective-C的东西,今天偶然看到一个有关MJ的视频,特此记录一下

一个NSObject对象占用多少内存字节
  • 首先回答这个问题,要分为两部分
    因为对象本质是个结构体,里面有isa指针指向自己所属的类(类的isa指针指向元类,元类的isa指针指向基类的meta-class对象),总归是个指针,那么,它占用的自己数是8(64bit位环境下)个字节没错了,所以通过class_getInstanceSize这个函数打印出来的是8个字节
  • 但是系统会对一个NSObject对象分配16个字节,通过malloc_size函数就可以打印出来

解析:
那我们接下来就看看这两个函数内部都是怎么调用的
首先我们来看 class_getInstanceSize这个函数的源码,源码在这里下载

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());
}

从这里我们就可以看出,本质上是调用alignedInstanceSize(),该函数描述为返回Class的ivar的大小,因为一个Class只有一个isa指针,故返回8

然后我们来看malloc_size,首先,该函数是什么时候调用的呢?当我们初始化一个实例对象的时候,例如NSObject *obj = [[NSObject alloc] init];其实本质上并不是调用的alloc()方法,本质上是调用的allocWithZone,对allocWithZone方法进行跟踪后发现

id objc_alloc(Class cls) 
{
    return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
id objc_allocWithZone(Class cls) 
{
    return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
     .....
     id obj = class_createInstance(cls,0);
}
id class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes,nil)

}
....
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) 
{
     ....
     size_t size = 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;
}

上述代码省略了非关注的问题,并且调整了顺序,当然,这些代码在源码以及编译成c/c++文件后都可以找到从,这里我们就能找到真正的原因,CoreFoundation要求所有的objects最少为16bit

对象的本质

接下来我们就要进入对OC中对象的研究,首先我们得搞清楚对象的分类

  1. 实例对象 instance class
  2. 类对象 class
  3. 元类对象 meta-class

为了方便理解,我们举个例子

Studen 学生类,Person人类,NSObject猿人类,首先我们将问题具象化,学生当然属于人类,人类有属于猿人类,继承关系我这里不上代码,比较懒,Student -> Person -> NSObject
简单粗暴的上结构图,首先我说明这些图是来自小马哥教育课件里的,我觉得小马哥做的都清晰明了
现有:Student *xiaoming = [[Student alloc] init];
那么xiaomingStudent创建的一个实例对象,xiaoming的内部结构为

xiaoming类
假设我们Student就已经有age这个属性了,上图就是xiaoming这个类里面存放的东西
那么Student里面又有什么呢?

元类对象里面放的什么信息呢
在这里插入图片描述
稍微解释下:
instance图里是xiaoming实例类的信息,以及存储的Student属性的值
class图是Student类信息的图,里面存放isa指针,superclass指针,属性信息、实例方法、协议信息,成员变量信息…
meta-class图是Student类的元类对象,存放isa指针,superclass指针,类方法信息…,下面要上一张重要的图,解释下isa,superClass这两个比较抽象的东西
在这里插入图片描述
这张图总结的比较好:
首先我们分析isa指针的指向(以Student为例,同理可分析Person,NSObject)

  • instance of Subclass其实就是指上面举的例子xiaoming,它的isa指向class,也就是上面举得例子Student
  • classisa指向meta-class 也就是Studentisa是指向Student的元类的
  • meta-classisa指向基类的meta-class,也就是说,Student的元类的isa是指向NSObject的元类的

接下来分析superclass指针的指向

  • classsuperclass指向父类的class在这里就是Studentsuperclass指向的是Person类
  • meta-classsuperclass指向父类的meta-class
  • 基类的meta-classsuperclass指向基类的class

然后我们分析下当xiaoming要调用自己的实例方法时的轨迹

  • 先通过xiaoming的类的isa指针找到Student,然后在Student实例方法列表中寻找实例方法,找到的话就调用,找不到的话,通过superclass在父类Person中去查找,找不到的话,在通过Person中的superclassPerson的父类中查找

最后我们分析下Student如何要调用类方法的轨迹

  • 先通过Student类中的isa指正找到Studentmeta-class,从meta-class中查找类方法,找不到的话,就通过superclass在元类的父类里面去找(按照上图已经找到NSObjectmeta-class),找不到的话,就返回到NSObject类中去查找方法(就是上图中Root class (meta) 又有箭头指向 Root class (class))
objc_class内部结构
struct objc_class {
    Class isa;
    Class superclass;
    cache_t cache;//方法缓存
    class_data_bits_t bits; //用于缓存具体类信息
};

bits里的内容如下

struct class_rw_t {
    uint32_5 flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t *methods;//方法列表
    property_list_t *properties;//属性列表
    const protocol_list_t * protocols;//协议列表
    Class fistSubclass;
    Class nextSiblingClass;
    char *demangledName;
}

const class_ro_t *ro;ro中具体的结构

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    unit32_t instanceSize;//instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif
    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;
};

上述结构为最新的objc源码中的结构,xcode里面的太旧了,早已经废弃

objc_class内部结构

问题:isa指针的地址 ?= ?元类对象的地址吗?
答案是否定的
正确答案: isa & ISA_MASK = 元类对象的地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS使用公钥字符串进行RSA加密的步骤如下: 1. 将公钥字符串转换为NSData类型。 ``` NSString *publicKeyString = @"-----BEGIN PUBLIC KEY-----\n...公钥字符串...\n-----END PUBLIC KEY-----"; NSData *publicKeyData = [publicKeyString dataUsingEncoding:NSUTF8StringEncoding]; ``` 2. 创建SecKey对象。 ``` NSMutableDictionary *publicKeyAttributes = [[NSMutableDictionary alloc] init]; [publicKeyAttributes setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; [publicKeyAttributes setObject:@(2048) forKey:(__bridge id)kSecAttrKeySizeInBits]; [publicKeyAttributes setObject:publicKeyData forKey:(__bridge id)kSecValueData]; [publicKeyAttributes setObject:(__bridge id)kSecAttrKeyClassPublic forKey:(__bridge id)kSecAttrKeyClass]; SecKeyRef publicKey; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKeyAttributes, (CFTypeRef *)&publicKey); ``` 3. 使用SecKey对象进行加密。 ``` NSData *plainData = [@"要加密的数据" dataUsingEncoding:NSUTF8StringEncoding]; size_t cipherBufferSize = SecKeyGetBlockSize(publicKey); uint8_t *cipherBuffer = malloc(cipherBufferSize); memset(cipherBuffer, 0, cipherBufferSize); OSStatus status = SecKeyEncrypt(publicKey, kSecPaddingPKCS1, [plainData bytes], [plainData length], cipherBuffer, &cipherBufferSize); NSData *encryptedData = [NSData dataWithBytesNoCopy:cipherBuffer length:cipherBufferSize]; ``` 4. 释放SecKey对象。 ``` CFRelease(publicKey); ``` 注意:在iOS使用公钥加密时,需要使用PKCS#1填充模式(kSecPaddingPKCS1)。另外,如果公钥字符串的换行符不是`\n`,需要将其替换为`\n`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值