当前位置: 首页 > 科技观察

说说iOSOC对象的内存对齐原理

时间:2023-03-21 14:10:10 科技观察

本文转载自微信公众号《网络开发》,作者刚冬冬。转载本文请联系网路发展公众号。引入问题初始化一个OC类,属性如下:#importNS_ASSUME_NONNULL_BEGIN@interfaceLGTeacher:NSObject@property(nonatomic,copy)NSString*name;@property(nonatomic,assign)intage;@property(nonatomic,assign)longheight;@property(nonatomic,strong)NSString*hobby;@endNS_ASSUME_NONNULL_END初始化对象,获取对象内存大小:LGTeacher*p=[[LGTeacheralloc]init];p.name=@"LG_Cooci“;页。age=18;p.height=185;p.hobby=@"female";NSLog(@"%lu-%lu",class_getInstanceSize([pclass]),malloc_size((__bridgeconstvoid*)(p)));打印结果:从上面的打印结果可以看出,class_getInstanceSize和malloc_size获取的内存大小是不一样的,那么是什么原因导致两者对同一个对象获取的内存大小不同呢?接下来我们继续探索。首先我们手动计算一下这个对象占用的内存:isa--8bytesname--8bytesage--4bytesheight--8byteshobby--8bytes,一共36bytes。我们追溯objc源码,发现有两个地方可以改变大小:instanceSize继续追踪1.instanceSizesize_tinstanceSize(size_textraBytes)const{if(fastpath(cache.hasFastInstanceSize(extraBytes))){returncache.fastInstanceSize(extraBytes);}size_tsize=alignedInstanceSize()+extraBytes;//alignedInstanceSize//CFrequiresallobjectsbeatleast16bytes.if(size<16)size=16;returnssize;}uint32_talignedInstanceSize()const{returnword_align(unalignedInstanceSize());}#defineWORD_MASK7ULstaticinlineuint32_tword_align(uint32(tx){returnx+WORD_MASK)&~WORD_MASK;}instanceSize可以从上面的源码中得到。大小按照8字节对齐原则处理,最小为16字节。2.calloc由于calloc属于malloc源码,跟踪libmalloc源码:calloc源码实现:void*calloc(size_tnum_items,size_tsize){void*retval;retval=malloc_zone_calloc(default_zone,num_items,size);if(retval==NULL){errno=ENOMEM;}returnretval;}//malloc_zone_callocvoid*malloc_zone_calloc(malloc_zone_t*zone,size_tnum_items,size_tsize){MALLOC_TRACE(TRACE_calloc|DBG_FUNC_START,(uintptr_t)zone,num_items,size,0);void++check_ptroc&ocer_count_start(=malloc_check_start)){internal_check();}ptr=zone->calloc(zone,num_items,size);if(malloc_logger){malloc_logger(MALLOC_LOG_TYPE_ALLOCATE|MALLOC_LOG_TYPE_HAS_ZONE|MALLOC_LOG_TYPE_CLEARED,(uintptr_t)zonet*(uintptr_t)zonet*(uintptr_t)zonet*(uintptr_t),0,(uintptr_t)ptr,0);}MALLOC_TRACE(TRACE_calloc|DBG_FUNC_END,(uintptr_t)zone,num_items,size,(uintptr_t)ptr);returnptr;}断点打印zone->calloc①:获取其真正的调用为default_zone_calloc②:搜索default_zone_calloc继续跟进,打印zone->callocindefault_zone_calloc获取nano_calloc③:分析nano_calloc源码可以知道在_nano_malloc_check_clear,size_tnum_items,size_tsize){size_ttotal_bytes;if(calloc_get_size(num_items,size,0,&total_bytes)){returnNULL;}if(total_bytes<=NANO_MAX_SIZE){void*p=_nano_malloc_check_clear(nanozone,total_bytes,1);if(p){returnp;}else{/*FALLTHROUGHtohelperzone*/}}malloc_zone_t*zone=(malloc_zone_t*)(nanozone->helper_zone);returnzone->calloc(zone,1,total_bytes);}跳转到_nano_malloc_check_clear,发现里面代码很多,一脸懵逼,仔细一看,很多都在做一些容错判断。去掉这些代码后,只有一行与size相关的代码:size_tslot_bytes=segregated_size_to_fit(nanozone,size,&slot_key);跳进segregated_size_to_fit看又是内存对齐的代码,这里的内存对齐是按照16字节的原则对齐的#defineSHIFT_NANO_QUANTUM4#defineNANO_REGIME_QUANTA_SIZE(1<>SHIFT_NANO_QUANTUM;//roundupandshiftfornumberofquantaslot_bytes=k<