[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$foStBX6PXr-6MS2iKE_2QRqHan8KZRK2vdcNpchsYUBo":3},{"code":4,"message":5,"data":6},200,"成功",{"id":7,"createdAt":8,"title":9,"content":10,"summary":11,"image":12,"uid":13,"user":14,"categoryId":21,"category":22,"subCategoryId":24,"subCategory":25,"comments":27,"status":17,"reason":12,"notice":12,"visitCount":28,"commentCount":29,"keywords":30},60,"2024-02-01T05:52:07.251Z","iOS的内存管理和runloop","# 内存管理 & Runloop\n## 一、内存管理\n1.  自动引用计数Related Count会自动给对象加retain，release.\n2.  函数是`objc_retain`\n```\nobjc_retain(id _Nullable objc)\n{\n    if (!obj) return obj;\n    if (obj->isTaggedPointer()) return obj;\n    return obj->retain();\n}\n\n```\n*   传进需要引用计数的对象\n*   isa指针是不是 TaggedPointer\n*   TaggedPointer针对小类型，当我们存储，比如NSNumber、NSString，字符串的长度小于固定长度时，我们使用TaggedPointer来优化它们的isa指针.目的是，存储的更高效.\n*   并不是所有对象的isa指针类型都是一样的\n*   所以对于NSNumber、NSString不用进行内存引用计数加1，内存管理语义是不一样的\n*   isa指针，64位，如果都用来存储地址，是不明智的。还存储了别的信息.\n\n- [ ]  retain函数\n```\nobjc_object::retain()\n{\n  assert(!isTaggedPointer());\n  if (fastpath(!ISA()->hasCustomRR())) {\n      return rootRetain();\n  }\n  \n  return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);\n}\n```\n- [ ] rootRetain()\n```\nobjc_object::rootRetain(bool tryRetain, bool handleOverflow)\n{\n....\n}\n```\n*   操作了sideTable: \\\n    是HashTable，存储的是我们当前对象的引用计数，弱引用表，锁\n\n*   执行了函数\n```\nid objc_object::sidetable_retain()\n{\n...\n}\n```\n\nSideTable的定义\n```\n*   // Template parameters.\n    enum HaveOld { DontHaveOld = false, DoHaveOld = true };\n    enum HaveNew { DontHaveNew = false, DoHaveNew = true };\n\n    struct SideTable {\n      spinlock\\_t slock;\n      // 引用计数，也是一个hash表\n      RefcontMap refcnts;\n      // 弱引用表，hash表\n      weak\\_table\\_t weak\\_table;\n      SideTable() {\n          memset(&weak_table, 0, sizeof(weak_table));\n      }\n      \n      ~SideTable() {\n          _objc_fatal(\"Do not delete SideTabel.\");\n      }\n      \n      void lock() { slock.lock(); }\n      void unlock() { slock.unlock(); }\n      void forceReset() { slock.forceReset(); }\n      \n      // Address-ordered lock discipline for a pair of side tables.\n      \n      template\u003CHaveOld, HaveNew>\n      static void lockTwo(SideTabel *lock1, SideTabel *lock2);\n      template\u003CHaveOld, HaveNew>\n      static void unlockTwo(SideTabel *lock1, SideTabel *lock2);\n}\n```\n\n*   引用计数加1\n\n    // 位移运算\n    refcntStorage += SIDE\\_TABLE\\_RC\\_ONE;\n\n这里的1，是经过位移后的1.\n\n\nweak：\n*   Runtime维护了一张全局的弱引用表\n*   调用了`id objc_initWeak(id *location, id newObj)`函数，location是地址，然后其中调用了\n    `storeWeak`方法来存储\n*   根据传进来的newobj来操作SideTabel来操作.\n*   在弱引用表中插入的是`weak_entry_t`，找到表起始地址，while循环找到我们存储的对象.\n*   有性能情况，不断的通过hash运算来查询、插入、删除等.\n*   总之，weak就是通过hash运算找到弱引用表，插入或者删除\n*   dealloc: 自动清理弱引用表.\n\n## Runloop\n1. 使用NSRunLoop currentLoop，是对CFRunLoop的封装.\n```\nstruct __CFRunLoop {\n  ...\n  // 唤醒\n  // mach_port内核通讯\n  __CFPort _wakeUpPort; // used for CFRunLoopWakeUp\n  ...\n  pthread_t _pthread;\n  ...\n  CFMutableSetRef _commonModes;\n  // timer，source， observer\n  CFMutableSetRef _commonModeItems;\n  CFRunLoopModeRef _currentMode;\n  ...\n}\n```\n\n2. 线程和RunLoop一一对应.\n3. CommonMode并不是mode类型，而是一个特殊标识\n```\nstruct __CFRunLoopMode {\n    ...\n    CFMutableSetRef _source0; // 不具备主动唤醒线程能力，手动唤醒线程。可创建并添加到当前的runloop中的\n    CFMutableSetRef _source1; // 基于port端口事件，可主动唤醒线程\n    ...\n}\n```\n\n### 示例解说\n```\nDispatch\\_async(dispatch\\_get\\_main\\_queue(), ^{\n  NSLog(@\"1\");\n  [self performSelector:@selector(test) withObject\\:nil afterDelay:0];\n  NSLog(@\"3\");\n})\n\n(void)test {\n  NSLog(\"current Thread -- %@\", \\[NSThread currentThread]);\n  NSLog(@\"2\");\n}\n\n```\n结果输出：\n```\n1 \n3 \ncurrent thread -- \u003CNSThread: ..>{number = 1, name = main}\n2\n```\n*   查看performSelector的注释，就是往当前线程中的RunLoop中注册了一个NSTimer.\n*   performSelector要求, 运行在default mode中，并且当前的runloop是执行的\n*   主线程默认runloop开启\n*   当runloop回来的时候，timer时间到了，再去执行selector，也就是performselector就是注册了一个timer，然后等待timer时间到了，回到default mode中时，再执行selector.\n\n注：如果是在dispatch_get_global_queue中时，输出: \n```\n1 \n3\n```\n原因：\nglobal队列的runloop默认不开启.\n\n","如何理解iOS的内存管理，什么是自动引用计数，sidetable又是什么，本文从源码的角度一步步和大家一起来进行探讨。","",499668042977349,{"phone":15,"userId":13,"nickName":16,"vipType":17,"avatar":18,"sign":19,"createdAt":20},"13121171998","全栈老韩",1,"https://image.xinwei.ltd/images/IMG_5430.JPG","全栈工程师，擅长iOS App开发、前端（vue、react、nuxt、小程序&Taro）开发、Flutter、React Native、后端（midwayjs、golang、express、koa）开发、docker容器、seo优化等。","2024-01-01T16:14:30.305Z",2,{"id":21,"name":23},"IT技术",3,{"id":24,"name":26,"parentId":21},"iOS",[],72,0,"ios,ios memory,ios auto reference,ios自动引用计数"]