如果访问用 __weak 修饰符的变量,那么其引用的对象会被加入到autoreleasepool ?
如果大量地使用__weak修饰符的变量,注册到autoreleasepool的对象也会大量增加 ?
前言
有关objc_autoreleaseReturnValue & objc_retainAutoreleasedReturnValue的知识点若还不清楚、还请先自行查找;
先来看下如下的代码:
{
NSMutableArray *array = [NSMutableArray array];
array = [NSMutableArray array];
array = [NSMutableArray array];
array = [NSMutableArray array];
/**
以上这4个array是4个不同的对象;
上面生成的array不会被加入到autoreleasePool中去;
*/
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
{
UIImage *image = [UIImage imageNamed:@"emoji_default"];
image = [UIImage imageNamed:@"emoji_default"];
image = [UIImage imageNamed:@"emoji_default"];
image = [UIImage imageNamed:@"emoji_default"];
/**
这4个image均执向同一个对象 (和image的缓存有关);
上面生成的image会被加入到autoreleasePool中去;
添加到autoreleasePool中的image的个数为: (N-1); // N表示image的个数
被注册到autoreleasePool中的对象除了image还会包含UITraitCollection、CUIMutableStructuredThemeStore等其他对象;
*/
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
所以如果在代码中的某一块需要频繁的使用 [UIImage imageNamed:]方法时、最好要用@autoreleasePool{}包裹一下、要不然可能会导致内存占用峰值的激增。
子线程与AutoreleasePool有什么关系?
在新建的子线程中是被autoreleasePool包含的,当线程退出的时候会清空autoreleasePool。
参考:stackoverflow
虽然说在子临时线程中会进行autorelease变量的回收、但是在创建子线程的时候、你的代码最好还是写在@autoreleasePool{}中,总不至于等到线程exit的时候再回收垃圾吧,你品一下常驻线程~ 如果等到线程退出再回收垃圾那岂不是早就死翘翘了。
再补充一句:创建常驻子线程的时候、最好还是用NSRunloop、少用CFRunloop的api。
每一个线程都会维护自己的 autoreleasepool 堆栈,换句话说 autoreleasepool 是与线程紧密相关的,每一个 autoreleasepool 只对应一个线程。
什么情况下访问用 __weak 修饰符的变量,那么其引用的对象会被加入到autoreleasepool ?
- (void)call_in_main { // 主线程中调用
TestObject *obj = [[TestObject alloc] init];
__weak id weakObj = obj; // obj | weakObj不会被添加到autoreleasePool中去
[weakObj noAction];
NSLog(@"weakObj: %@",weakObj);
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
下面来针对TestObject中的3个实例方法进行测试、本文将会在子线程中进行测试(主线程中太多对象被注册到了autoreleasePool中去、不利于调试与分析):
- (NSMutableString *)testMethod_2 {
NSMutableString *name = [[NSMutableString alloc] initWithString:@"Avery"];
NSLog(@"testMethod_2 - name: %p", name);
return name;
}
- (NSMutableArray *)testMethod_3 {
NSMutableArray *array = [NSMutableArray array];
[array addObject:@"Avery"];
NSLog(@"testMethod_3 - array: %p", array);
return array;
}
- (void)testMethod_4 {
for (NSUInteger i = 0; i < 3; i++) {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
[array addObject:[NSString stringWithFormat:@"Avery - %ld",i]];
NSLog(@"testMethod_4 - array: %p", array);
}
}
- (void)call_in_subThread {
TestObject *obj = [[TestObject alloc] init];
// [obj testMethod_2]; // 用obj调用时不会使testMethod_2方法中生成的 MutableString 被添加到autoreleasePool中
__weak id weakObj = obj;
[weakObj testMethod_2]; // 用weakObj调用时会使testMethod_2方法中生成的 MutableString 被添加到autoreleasePool中去
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
TestObject *obj = [[TestObject alloc] init];
__weak id weakObj = obj;
[weakObj testMethod_4]; // 用weakObj调用时不会使testMethod_4方法中生成的 NSMutableArray 被添加到autoreleasePool中
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
TestObject *obj = [[TestObject alloc] init];
// [obj testMethod_3]; // 用obj调用时不会使testMethod_3方法中生成的 NSMutableArray 被添加到autoreleasePool中
__weak id weakObj = obj;
[weakObj testMethod_3]; // 用weakObj调用时会使testMethod_3方法中生成的 NSMutableArray 被添加到autoreleasePool中去
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
- (void)call_in_subThread {
TestObject *obj = [[TestObject alloc] init];
__weak id weakObj = obj;
[obj testMethod_2];
[obj testMethod_3];
[obj testMethod_4];
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
}
AveryTest[56451:20807849] currentThread(autoreleasePool_Action): <NSThread: 0x600001439d80>{number = 1, name = main}
AveryTest[56451:20807927] testMethod_2 - name: 0x600000f52310
AveryTest[56451:20807927] testMethod_3 - array: 0x600000f5d590
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f523d0
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f6adc0
AveryTest[56451:20807927] testMethod_4 - array: 0x600000f5d4a0
objc[56451]: ##############
objc[56451]: AUTORELEASE POOLS for thread 0x700009a7c000
objc[56451]: 3 releases pending.
objc[56451]: [0x7fda1b02d000] ................ PAGE (hot) (cold)
objc[56451]: [0x7fda1b02d038] ################ POOL 0x7fda1b02d038
objc[56451]: [0x7fda1b02d040] 0x600000f52310 __NSCFString
objc[56451]: [0x7fda1b02d048] 0x600000f5d590 __NSArrayM
objc[56451]: ##############
用__weak弱引用的对象什么时候会被释放?看下面这个例子:
{
/**
__weak id _reference_short;
__weak id _reference_long;
__weak id _reference_array_alloc;
*/
NSString *str_short = [NSString stringWithFormat:@"Avery"]; // TaggedPointer
NSString *str_long = [NSString stringWithFormat:@"Avery_iPhone3GS&iPhone5S&iPhoneSE"]; // 被注册到autoreleasepool
NSMutableArray *array_0 = [[NSMutableArray alloc] init]; // 不会被注册到autoreleasepool
// NSMutableArray *array_1 = [NSMutableArray arrayWithObjects:@"Avery", nil]; // 被注册到autoreleasepool
_reference_short = str_short;
_reference_long = str_long; // str_long是一个autorelease对象,设置一个weak的引用来观察它
_reference_array_alloc = array_0;
/**
extern void _objc_autoreleasePoolPrint(void); // 打印注册到autoreleasePool中的对象
_objc_autoreleasePoolPrint();
*/
}
TEST[57881:21330279] _reference_short (-viewWillAppear-): Avery
TEST[57881:21330279] _reference_long (-viewWillAppear-): Avery_iPhone3GS&iPhone5S&iPhoneSE
TEST[57881:21330279] _reference_array_alloc (-viewWillAppear-): (null)
TEST[57881:21330279] currentMode (-viewWillAppear-): kCFRunLoopDefaultMode
TEST[57881:21330279]
TEST[57881:21330279] _reference_short (+viewDidAppear+): Avery
TEST[57881:21330279] _reference_long (+viewDidAppear+): (null)
TEST[57881:21330279] _reference_array_alloc (+viewDidAppear+): (null)
TEST[57881:21330279] currentMode (+viewDidAppear+): kCFRunLoopDefaultMode
猜猜上面的这个弱引用的_reference_long的释放时机是什么时候?
如上图所示:当App启动后苹果会在主线程的 RunLoop 里注册两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池、其 order 是-2147483647优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件:
(1) BeforeWaiting(将要进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的autoreleasePool并创建新的autoreleasePool;
(2) Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放autoreleasePool,这个 Observer 的 order 是 2147483647优先级最低,保证其释放池子发生在其他所有回调之后。
此处_reference_long的释放就发生在runloop进入到BeforeWaiting状态时。
通过上面这几个例子你发现了什么?
(1) 当用weak指针修饰的对象调用其方法时、该方法中产生的对象才有可能会被加入到autoreleasePool中去(当方法中的返回值是对象时);
(2) 当用强引用指针修饰的对象调用其方法时(同时又有弱指针与其指向同一块内存)、
该方法中产生的对象有可能会被加入到autoreleasePool中去(当方法中的返回值是对象时);
(3) __weak修饰的指针本身就指向一个autorelease对象时。
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
迷惑行为大赏
在某度搜索"如果大量地使用__weak修饰符的变量,注册到autoreleasepool的对象也会大量增加"、对排在前面的博文进行了截屏:几乎所有的博文都是这样写的或者基本上或者大同小异 (以下代码摘抄于该文章):
{
//obj是__strong修饰且被赋值的对象
id __weak obj1 = obj;
NSLog(@"%@", obj1);
}
// 编译器的模拟代码
id __weak obj1; // 【原博文中这里少了一个__weak】
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", obj1);
objc_destroyWeak(&obj1); // 作用域结束,释放对象
我没记错的话现在都2020年了吧 (或者至少都2020年了)、系统都iOS13.x了。。。所以当你看4 ~ 5年前的帖子的时候请务必多留个心眼!这也是为什么我不让别人直接转载我的博文的原因、如果文章中有错误、我改正了但是谁又能保证转载的文章的质量呢。
美国总统特朗普说喝消毒液可以杀死新冠病毒、你咋不去跟风喝二两呢?
不要人云亦云要留有自己的判断力。
编译后的代码到底是什么样的?到底会不会加入到autoreleasepool中?试一下不就知道了嘛 (环境:XCode 11.3.1 时间:2020.04.30)。。。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
TestObject *obj = [[TestObject alloc] init];
[obj testAction];
/**
_objc_alloc // TestObject -> alloc
_objc_msgSend // TestObject类alloc出的对象 -> init
_objc_msgSend // TestObject类alloc出的对象 -> testAction
_objc_storeStrong // 出了作用域、销毁"TestObject类alloc出的对象"
*/
});
{
NSObject *obj = [[NSObject alloc] init];
__weak id weakObj = obj;
NSLog(@"weakObj: %@",weakObj);
/**
_objc_alloc // NSObject -> alloc
_objc_msgSend // NSObject类alloc出的对象 -> init
_objc_initWeak // __weak id weakObj = obj;
_objc_loadWeakRetained // id tmp = objc_loadWeakRetained(&weakObj); (此函数取出附有__weak修饰符变量所引用的对象并进行retain)
_NSLog
_objc_release // _objc_release(tmp)
_objc_destroyWeak // objc_destroyWeak(&weakObj);
_objc_storeStrong // 出了作用域、销毁obj对象, _objc_storeStrong(&obj);
_objc_destroyWeak // 出了作用域、销毁weakObj对象, objc_destroyWeak(&weakObj);
*/
}
【 请勿直接转载 - 节约能源从你我做起 】