iOS 底层面试

iOS内存管理

2021-04-15  本文已影响0人  lieon

CADisplayLink、NSTimer使用注意

第三方代理对象的两种形式

@interface Proxy : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end

#import "Proxy.h"
@implementation Proxy

+ (instancetype)proxyWithTarget:(id)target
{
  Proxy *proxy = [[Proxy alloc] init];
  proxy.target = target;
  return proxy;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
  return self.target;
}

@end
#import <Foundation/Foundation.h>

@interface Proxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end

#import "Proxy.h"
@implementation Proxy

+ (instancetype)proxyWithTarget:(id)target
{
  // NSProxy对象不需要调用init,因为它本来就没有init方法
  Proxy *proxy = [Proxy alloc];
  proxy.target = target;
  return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
  return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
  [invocation invokeWithTarget:self.target];
}

@end

GCD定时器


#import "GCDTimer.h"

@implementation GCDTimer

static NSMutableDictionary *timers_;
dispatch_semaphore_t semaphore_;
+ (void)initialize
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timers_ = [NSMutableDictionary dictionary];
        semaphore_ = dispatch_semaphore_create(1);
    });
}

+ (NSString *)execTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
    if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
    
    // 队列
    dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
    
    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置时间
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
                              interval * NSEC_PER_SEC, 0);
    
    
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 定时器的唯一标识
    NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
    // 存放到字典中
    timers_[name] = timer;
    dispatch_semaphore_signal(semaphore_);
    
    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
        task();
        
        if (!repeats) { // 不重复的任务
            [self cancelTask:name];
        }
    });
    
    // 启动定时器
    dispatch_resume(timer);
    
    return name;
}

+ (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
    if (!target || !selector) return nil;
    
    return [self execTask:^{
        if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [target performSelector:selector];
#pragma clang diagnostic pop
        }
    } start:start interval:interval repeats:repeats async:async];
}

+ (void)cancelTask:(NSString *)name
{
    if (name.length == 0) return;
    
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    
    dispatch_source_t timer = timers_[name];
    if (timer) {
        dispatch_source_cancel(timer);
        [timers_ removeObjectForKey:name];
    }

    dispatch_semaphore_signal(semaphore_);
}

@end

iOS程序的内存布局

iOS程序的内存布局图

Tagged Pointer


#if TARGET_OS_OSX && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#else
#   define _OBJC_TAG_MASK 1UL
#endif

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr) 
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
- (void)setName:(NSString *)name
{
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}

- (void)test {
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            // 加锁
            self.name = [NSString stringWithFormat:@"abcdefghijk"];
            // 解锁
        });
    }
- (void)test {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abc"];
        });
    }
}

- (void)test {
    NSString *str1 = [NSString stringWithFormat:@"abcdefghijk"];
    NSString *str2 = [NSString stringWithFormat:@"ab"];
    
    NSLog(@"%@ %@", [str1 class], [str2 class]); // __NSCFString NSTaggedPointerString
}

OC对象的内存管理

Copy关键字

@interface Person : NSObject
@property (copy, nonatomic) NSMutableArray *data;
@property (strong, nonatomic) NSMutableArray *data1
@end

#import "Person.h"

@implementation Person

- (void)setData:(NSArray *)data {
    if (_data != data) {
        [_data release];
        _data = [data copy]; // 调用copy之后, _data 变为NSArray, 如果外部对dat数组进行添加元素,会出现unrecognise selector崩溃
    }
}

- (void)setData1:(NSMutableArray *)data1 {
    if (!_data1 != data1) {
        [_data1 release];
        _data1 = [data1 retain] // 用strong修饰之后,data1的引用计数加1,依然是NSMutableArray
    }
}

- (void)dealloc {
    self.data = nil;
    [super dealloc];
}

@end

自定义对象的copy

引用计数的存储

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
    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;
        uintptr_t extra_rc          : 19;
    };
};

struct SideTable {
    // 保证原子操作的自旋锁
    spinlock_t slock;
    // 引用计数的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
}
inline uintptr_t 
objc_object::rootRetainCount()
{
    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) {
            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;
}

SideTable

struct SideTable {
    // 保证原子操作的自旋锁
    spinlock_t slock;
    // 引用计数的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
}

weak 表 --- weak_table_t

struct weak_table_t {
    //  hash数组,用来存储弱引用对象的相关信息weak_entry_t
    weak_entry_t *weak_entries;
    // 存储空间
    size_t    num_entries;
    // 参与判断引用计数辅助量
    uintptr_t mask;
    // hash key 最大偏移值
    uintptr_t max_hash_displacement;
};

weak_entry_t

struct weak_entry_t {
    DisguisedPtr<objc_object> referent; // 被弱引用的对象
    union {
        struct {
            weak_referrer_t *referrers;  // 弱引用该对象的对象指针地址的hash数组
            uintptr_t        out_of_line_ness : 2; // 是否使用动态hash数组标记位
            uintptr_t        num_refs : PTR_MINUS_2; // hash数组中的元素个数
            uintptr_t        mask;  // hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)素个数)。
            uintptr_t        max_hash_displacement; //  // 可能会发生的hash冲突的最大次数,用于判断是否出现了逻辑错误(hash表中的冲突次数绝不会超过改值)
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    // 声明两个SideTable
    // 新旧散列表的创建
    SideTable *oldTable;
    SideTable *newTable;

    // 获得新值和旧值的锁位置
    // 通过地址来建立索引标识,防止桶重复
    // 下面的指向操作会改变旧值
 retry:
    if (haveOld) {
        // 更改指针,获得以oldObj为索引所存储的值地址
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        // 更改指针,获得以newObj为索引所存储的值地址
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }
    // 加锁操作,防止多线程中竞争冲突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
    // location应该与oldObj保持一致,如果不同,说明当前的location已经处理过oldObj,可是又被其他线程修改
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // 防止弱引用间死锁
    // 并且通过+initialize初始化构造器所保证所有弱引用的isa非空指向
    if (haveNew  &&  newObj) {
        // 获取新对象的isa
        Class cls = newObj->getIsa();
        // 判断isa非空且已经初始化
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            // 解锁
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            // 对isa指针进行初始化
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
            // 如果该类已经完成执行 +initialize 方法是最理想情况
            // 如果该类 +initialize 在线程中
            // 例如 +initialize 正在调用 storeWeak 方法
            // 需要手动对其增加保护策略,并设置 previouslyInitializedClass 指针进行标记
            previouslyInitializedClass = cls;
            // 重新尝试
            goto retry;
        }
    }

    // 清除旧值
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // 分配新值
    if (haveNew) {
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
    
        // 如果弱引用被释放 weak_register_no_lock 方法返回 nil
        // 在引用计数表中设置若引用标记位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            // 弱引用位初始化操作
            // 引用计数那张散列表的weak引用对象的引用计数中标识为weak引用
            newObj->setWeaklyReferenced_nolock();
        }
        // 之前不要设置 location 对象,这里需要更改指针指向
        *location = (id)newObj;
    }
    else {
        // 没有新值,则无需更改
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}

旧对象解除注册操作 weak_unregister_no_lock

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    // 用指针去访问 oldObj 和 *location
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;
    // 如果其对象为 nil,无需取消注册
    if (!referent) return;
    // weak_entry_for_referent 根据首对象查找 weak_entry
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 通过地址来解除引用关联
        remove_referrer(entry, referrer);
        bool empty = true;
        // 检测 out_of_line 位的情况
        // 检测 num_refs 位的情况
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            // 将引用表中记录为空
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }
        // 从弱引用的 zone 表中删除
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }
    // 这里不会设置 *referrer = nil,因为 objc_storeWeak() 函数会需要该指针
    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

新对象添加注册操作 weak_register_no_lock

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{   // 用指针去访问 oldObj 和 *location
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;
    // 检测对象是否生效、以及是否使用了 tagged pointer 技术
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // 保证引用对象是否有效
    // hasCustomRR 方法检查类(包括其父类)中是否含有默认的方法
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        // 检查 dealloc 状态
        deallocating = referent->rootIsDeallocating();
    }
    else {
        // 会返回 referent 的 SEL_allowsWeakReference 方法的地址
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }
    // 由于 dealloc 导致 crash ,并输出日志
    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // 记录并存储对应引用表 weak_entry
    weak_entry_t *entry;
    // 对于给定的弱引用查询 weak_table
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        //  增加弱引用表于附加对象上
        append_referrer(entry, referrer);
    } 
    else {
        // // 自行创建弱引用表
        weak_entry_t new_entry(referent, referrer);
        // 如果给定的弱引用表满容,进行自增长
        weak_grow_maybe(weak_table);
        // 向对象添加弱引用表关联,不进行检查直接修改指针指向
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}
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) {
       //如果isa中标记了是若引用,则 从weak_table中清除当前对象的弱引用
       weak_clear_no_lock(&table.weak_table, (id)this);
   }
   if (isa.has_sidetable_rc) {
       // 如果isa中标记了是has_sidetable_rc,则 从引用计数散列表中清除当前的引用计数
       table.refcnts.erase(this)
   }
   table.unlock();
}

自动释放池

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */
    {
        __AtAutoreleasePool __autoreleasepool;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_crl5bnj956d806cp7d3ctqhm0000gn_T_main_d37e0d_mi_0);
     }//大括号对应释放池的作用域
     
     return 0;
}

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
  ~__AtAutoreleasePool() {
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
    
  void * atautoreleasepoolobj;
};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ {
        void *atautoreleasepoolobj = objc_autoreleasePoolPush();
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_kb_06b822gn59df4d1zt99361xw0000gn_T_main_d39a79_mi_0);
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}
struct __AtAutoreleasePool {
  __AtAutoreleasePool() {
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
  ~__AtAutoreleasePool() {
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
    
  void * atautoreleasepoolobj;
};
void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}



AutoreleasePoolPage的结构

class AutoreleasePoolPage 
{
    magic_t const magic;                  //校验AutoreleasePagePoolPage结构是否完整
    id *next;                             //指向新加入的autorelease对象的下一个位置,初始化时指向begin()
    pthread_t const thread;               //当前所在线程,AutoreleasePool是和线程一一对应的
    AutoreleasePoolPage * const parent;   //指向父节点page,第一个结点的parent值为nil
    AutoreleasePoolPage *child;           //指向子节点page,最后一个结点的child值为nil
    uint32_t const depth;                 //链表深度,节点个数
    uint32_t hiwat;                       //数据容纳的一个上限
}


static inline void *push()
 {
     id *dest;
     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;
 }

理解objc_autoreleasePoolPush方法

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

  static inline void *push() 
    {
        id *dest;
        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;
    }

static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        // page存在且没有满,直接添加
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            // page存在且已满,则重新初始化一个新的page
            return autoreleaseFullPage(obj, page);
        } else {
            // page不存在,则创建一个新的hotPage
            return autoreleaseNoPage(obj);
        }
    }

  // 当前hotPage已满时调用
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        assert(page == hotPage());
        assert(page->full()  ||  DebugPoolAllocation);
        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());
        setHotPage(page);
        return page->add(obj);
    }

 // 当前hotPage不存在时调用
    id *autoreleaseNoPage(id obj)
    {
        assert(!hotPage());
        bool pushExtraBoundary = false;
        if (haveEmptyPoolPlaceholder()) {
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         pthread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            return setEmptyPoolPlaceholder();
        }
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);
        }
        return page->add(obj);
    }

 // 压栈操作:将对象加入AutoreleaseNoPage并移动栈顶的指针
    id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }

objc_autoreleasePoolPop方法

static inline void pop(void *token) 
    {
        AutoreleasePoolPage *page;
        id *stop;

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            if (hotPage()) {
                pop(coldPage()->begin());
            } else {
                setHotPage(nil);
            }
            return;
        }
        page = pageForPointer(token);
        stop = (id *)token;
        if (*stop != POOL_BOUNDARY) {
            if (stop == page->begin()  &&  !page->parent) {
            } else {
                return badPop(token);
            }
        }

        if (PrintPoolHiwat) printHiwat();
        // 向栈中的对象发送release消息,直到约到第一个POOL_BOUNDARY
        page->releaseUntil(stop);
        // 删除空掉的节点
        if (DebugPoolAllocation  &&  page->empty()) {
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            page->kill();
            setHotPage(nil);
        } 
        else if (page->child) {
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

autorelease方法

- [NSObject autorelease]
└── id objc_object::rootAutorelease()
    └── id objc_object::rootAutorelease2()
        └── static id AutoreleasePoolPage::autorelease(id obj)
            └── static id AutoreleasePoolPage::autoreleaseFast(id obj)
                ├── id *add(id obj)
                ├── static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
                │   ├── AutoreleasePoolPage(AutoreleasePoolPage *newParent)
                │   └── id *add(id obj)
                └── static id *autoreleaseNoPage(id obj)
                    ├── AutoreleasePoolPage(AutoreleasePoolPage *newParent)
                    └── id *add(id obj)

RunLoop和线程的关系

RunLoop和自动释放池的关系

Thread和自动释放池的关系

Runloop和Autorelease

AutoreleasePool子线程上的释放时机

AutoreleasePool需要手动添加的情况

- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 1000000; i++) {
        NSObject *obj = [[NSObject alloc] init];
    }
 }
- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 1000000; i++) {
        @autoreleasepool{
             NSObject *obj = [[NSObject alloc] init];
        }
    }
 }
上一篇 下一篇

猜你喜欢

热点阅读