OC源码:https://opensource.apple.com/tarballs/objc4/
定时器
- CADisplayLink (调用频率和帧率保持一致 60fps)
- NSTimer
当定时器为类属性时 ,且定时器target为该类,则会造成循环引用
定时器循环引用解决方案
(1).NSTimer用block
(2).timer的target指向otherObject,otherObject的target弱引用VC
NSProxy
NSProxy没有继承自NSObject,没有init方法,这个类专门用来做消息转发,此时的消息机制是先查找自己类中是否实现,有实现调用,没有实现的话不经过父类消息查找和动态方法解析,直接进入消息转发
继承自NSProxy 的类的实例对象,在调用isKindOfClass和isMemberOfClass的方法时,直接进入消息转发,NSProxy该类的实例对象适合做中间对象
image.png
GCD定时器
NSTimer依赖于Runloop,Runloop如果任务繁重的话,会导致NSTimer不准时,而GCD的定时器更准确一些
//主队列 事件在主线程跑
// dispatch_queue_t queue = dispatch_get_main_queue();
//并发队列,串行队列,全局并发队列 事件在子线程跑
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置时间 参数 :timer 多久后开始 时间间隔 时间误差
NSTimeInterval start = 2.0;
NSTimeInterval interval = 1.0;
//从现在开始
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, interval * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//从2.0秒后开始
// dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC) , interval * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
//回调事件
NSLog(@"hello");
});
//定时器开始
dispatch_resume(timer);
//取消定时器
dispatch_source_cancel(timer);
iOS程序的内存布局
从低地址到高地址排列
-
从0地址开始有一段系统保留
-
代码段(_TEXT)
存放编译之后的代码 -
数据段(_DATA)
存放字符串常量
已初始化的全局变量,静态变量等
未初始化数据 -
堆(heap)
通过alloc,malloc,calloc等动态分配的空间,分配的内存空间地址越来越大 -
栈(stack)
函数调用开销,比如局部变量。分配的内存空间地址越来越小 -
内核区
Tagged Pointer
- 从64位开始,iOS引入Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
- 在没有使用Tagged Pointer之前,NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
- 使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag+Data ,也就是将数据直接存储在了指针中
- 当指针不够存储数据时,才会使用动态分配堆内存的方式来存储数据
- objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据
-
判断一个指针是否为Tagged Pointer
iOS平台,最高有效位为1
Mac平台,最低有效位为1
image.png
OC对象的内存管理
- iOS中,使用引用计数来管理OC对象的内存
- 一个新创建的OC对象的引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
- 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
- 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者auto release来释放它
Copy
- iOS提供了两个拷贝方法
copy 不可变拷贝
mutableCopy 可变拷贝
NSString* sss = @"abc";
self.str = [sss copy]; //返回NSString
self.str1 = [sss mutableCopy]; //返回NSMutableString
sss = @"gfw";
NSLog(@"%@ -- %@",self.str,self.str1);
NSLog(@"%p -- %p",self.str,self.str1);
image.png
- NSString字符串
NSString* str = [NSString stringWithFormat:@"abc"];
NSString* str1 = [str copy]; //浅拷贝
NSMutableString* str2 = [str mutableCopy]; //深拷贝
image.png
NSMutableString* str = [NSMutableString stringWithFormat:@"abc"];
NSString* str1 = [str copy]; //深拷贝
NSMutableString* str2 = [str mutableCopy]; //深拷贝
image.png
-
NSArray和NSDictionary同上
image.png -
copy修饰的属性
@property (nonatomic,copy) NSMutableArray *array;
copy不要修饰可变类型,因为copy修饰的属性的自动生成的set方法,会对可变参数进行copy操作,将不可变对象再赋值给成员变量
- 自定义类的实例对象的copy
//
// ViewController.h
// 内存管理-copy
//
// Created by SivatiMac on 2018/12/9.
// Copyright © 2018 SivatiMac. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <NSCopying>
@property (nonatomic,strong) UIView* cuntomView;
@property (nonatomic,assign) int age;
@end
//
// ViewController.m
// 内存管理-copy
//
// Created by SivatiMac on 2018/12/9.
// Copyright © 2018 SivatiMac. All rights reserved.
//
#import "ViewController.h"
@implementation ViewController
-(id)copyWithZone:(NSZone *)zone{
ViewController* vc = [[ViewController allocWithZone:zone] init];
vc.cuntomView = [UIView new];
vc.age = self.age;
return vc;
}
@end
引用计数的存储
引用计数存储在对象的union isa中,如果isa中extra_rc不够存引用计数了,额外的引用计数就存在sidetable中
下面为查看objc4中的源码
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1; //sidetable中是否有引用计数
uintptr_t extra_rc : 19; //extra_rc+1为引用计数
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
};
image.png
inline uintptr_t
objc_object::rootRetainCount()
{
//如果是TaggedPointer对象,返回自己
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) {
//引用计数
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
//加上存在sidetable中额外的引用计数
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
size_t
objc_object::sidetable_getExtraRC_nolock()
{
assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
weak实现原理
SideTable& table = SideTables()[this];
该方法可以获取当前对象的SideTable,SideTable中存有该对象的弱引用表weak_table,weak_table是散列表,对象dealloc时,会遍历weak_table,将指向该对象的weak指针置为nil ,代码调用如下
- dealloc详细流程
查看objc4源码,对象在dealloc时候发生了什么事
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
查看_objc_rootDealloc()
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
查看rootDealloc()
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
/*
如果没有被弱引用 weakly_referenced
没有关联对象 has_assoc
没有c++析构函数 has_cxx_dtor
sidetable中没有引用计数 has_sidetable_rc
*/
free(this); //直接释放
}
else {
//否则调用object_dispose
object_dispose((id)this);
}
}
查看object_dispose()
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
在free前会执行objc_destructInstance(obj)
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
//移除成员变量
if (cxx) object_cxxDestruct(obj);
//移除关联对象
if (assoc) _object_remove_assocations(obj);
//将指向当前对象的弱指针置为nil
obj->clearDeallocating();
}
return obj;
}
查看clearDeallocating()
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
//isa非指针走这里
clearDeallocating_slow();
}
assert(!sidetable_present());
}
查看clearDeallocating_slow()
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
//清除弱引用表
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
查看 weak_clear_no_lock(&table.weak_table, (id)this)
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
//遍历弱引用表
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[I];
if (referrer) {
if (*referrer == referent) {
//将指向该对象的弱指针置为nil
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
autorelease和autoreleasepool
- autoreleasepool的主要底层数据结构是__AtAutoreleasePool和AutoreleasePoolPage
- 调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的
AutoreleasePoolPage
- 每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
- 所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
- 一个AutoreleasePoolPage对象存满了,会创建一个新的AutoreleasePoolPage对象,APP运行可能存在多个AutoreleasePoolPage对象
*查看autoreleasePool信息的私有函数
//
// main.m
// Created by SivatiMac on 2018/12/9.
// Copyright © 2018 SivatiMac. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
_objc_autoreleasePoolPrint();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
image.png
分析过程
在终端用clang重写main.m后在main.cpp中查看
指令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
可以看到,autoreleasepool结构体对象创建的时候调用objc_autoreleasePoolPush(),销毁的时候调用objc_autoreleasePoolPop(atautoreleasepoolobj)
struct __AtAutoreleasePool {
//构造函数
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
//析构函数
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
在objc4源码NSObject.mm中查看
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
查看AutoreleasePoolPage::push()
static inline void *push()
{
id *dest;
//第一次进来,如果没有pool配置
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
autoreleaseNewPage和autoreleaseFast方法都是生成AutoreleasePoolPage对象
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
查看AutoreleasePoolPage成员
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
查看autoreleaseFullPage(obj, page)
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that page.
assert(page == hotPage());
assert(page->full() || DebugPoolAllocation);
//寻找或创建一个不满的page
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
//设置这个不满的page为hotPage
setHotPage(page);
//将POOL_BOUNDARY加到page
return page->add(obj);
}
查看page->add(obj)
id *add(id obj)
{
assert(!full());
unprotect();
//next为page最后一个成员变量的下一个地址
id *ret = next; // faster than `return next-1` because of aliasing
//先将obj地址给next,再将next移到下一个
*next++ = obj;
protect();
//返回的是next移动前的地址,第一次是POOL_BOUNDARY的地址
return ret;
}
push方法创建page到此结束,接下来查看对象的autorelease方法
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
------------------------
// Base autorelease implementation, ignoring overrides.
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
------------------------
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
-------------------------
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
//autoreleaseFast方法是将对象加到page中
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
接下来查看自动释放池释放时执行的objc_autoreleasePoolPop方法都干了什么
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
-------------------------
void
objc_autoreleasePoolPop(void *ctxt)
{
//ctxt为传进来的atautoreleasepoolobj,也就是
AutoreleasePoolPage::pop(ctxt);
}
查看AutoreleasePoolPage::pop(ctxt)
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
if (hotPage()) {
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
page = pageForPointer(token);
//token也就是POOL_BOUNDARY的地址
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
//release page中的对象
page->releaseUntil(stop);
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
}
else if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
查看releaseUntil(stop)
void releaseUntil(id *stop)
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
//release对象
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}
autoreleasepool生命周期至此结束
runloop与autorelease
那么平时写的VC中的autorelease变量是什么时机被释放的呢
- MRC 程序运行时runloop会一直处于唤醒或者休眠状态,而局部变量的释放时机是在所属的那次runloop循环中,runloop休眠之前会调用AutoreleasePoolPage的pop方法,在这个方法中release
- ARC 由于编译器自动生成release代码,所以局部变量是出作用域之前立马释放
网友评论