一、block内存管理1.block内存类型block内存分为三种类型:
2.三种类型的内存的创建时机1)对于 void (^globalBlock)() = ^{ }; int main(int argc, const char * argv[]) { @autoreleasepool { void (^stackBlock1)() = ^{ }; } return 0; } 那么我们怎么确定这两个block,就是我们所说的两种类型的block呢,我们可以使用 // globalBlock struct __globalBlock_block_impl_0 { struct __block_impl impl; struct __globalBlock_block_desc_0* Desc; __globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteGlobalBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; ... // stackBlock struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; ... int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; void (*stackBlock)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA); } return 0; } 可以看出可以看出globalBlock是_NSConcreteGlobalBlock类型,即在全局区域创建,block变量存储在全局数据存储区;stackBlock是_NSConcreteStackBlock类型,即在栈区创建。
// 如果是weak类型的block,依然不会自动进行copy // <__NSStackBlock__: 0x7fff5fbff728> __weak void (^weakBlock)() = ^{i;}; // ARC情况下输出 // <__NSMallocBlock__ NSLog(@"%@", [self callBack:weakBlock]); - (id)callBack:(void (^)(void))callBack { NSLog(@"%@", callBack); return callBack; } //输出结果 <__NSStackBlock__: 0x7ffee2559838> <__NSMallocBlock__: 0x600003a99ce0>
@property (copy, nonatomic) id myCopyBlock; @property (strong, nonatomic) id myStrongBlock; // 如果是weak类型的block,依然不会自动进行copy // <__NSStackBlock__: 0x7fff5fbff728> __weak void (^weakBlock)() = ^{i;}; NSLog(@"%@", weakBlock); //会进行copy操作 //<__NSMallocBlock__: 0x6000037e8db0> self.myCopyBlock = weakBlock; NSLog(@"%@", self.myCopyBlock); // 会进行strong操作 // <__NSStackBlock__: 0x7fff5fbff728> self.myStrongBlock = weakBlock; NSLog(@"%@", self.myStrongBlock); //打印结果 //<__NSStackBlock__: 0x7ffee8ed5838> //<__NSMallocBlock__: 0x6000037e8db0> //<__NSStackBlock__: 0x7ffee8ed5838>
int i = 10; void (^block)() = ^{i;}; // 因为block为strong类型,且捕获了外部变量,所以赋值时,自动进行了copy // <__NSMallocBlock__: 0x100206920> NSLog(@"%@", block); 对于作为参数传递的block,其类型是什么呢? int i = 10; void (^block)() = ^{i;}; __weak void (^weakBlock)() = ^{i;}; void (^stackBlock)() = ^{}; // ARC情况下 // 创建时,都会在栈中 // <__NSStackBlock__: 0x7fff5fbff730> NSLog(@"%@", ^{i;}); // 因为block为strong类型,且捕获了外部变量,所以赋值时,自动进行了copy // <__NSMallocBlock__: 0x100206920> NSLog(@"%@", block); // 如果是weak类型的block,依然不会自动进行copy // <__NSStackBlock__: 0x7fff5fbff728> NSLog(@"%@", weakBlock); // 如果block是strong类型,并且没有捕获外部变量,那么就会转换成__NSGlobalBlock__ // <__NSGlobalBlock__: 0x100001110> NSLog(@"%@", stackBlock); [self callBack:weakBlock]; [self callBack:block]; [self callBack:stackBlock]; - (id)callBack:(void (^)(void))callBack { NSLog(@"%@", callBack); return callBack; } //结果 //<__NSStackBlock__: 0x7ffee2572838> //<__NSMallocBlock__: 0x600002e881e0> // <__NSGlobalBlock__: 0x10d68c0f8> //<__NSStackBlock__: 0x7ffee2572838> //<__NSMallocBlock__: 0x600002e881e0> //<__NSGlobalBlock__: 0x10d68c0f8> 我们可以发现函数参数的block为什么类型,block在函数中就是什么类型。 二、autorelease内存管理1、哪些对象是autorelease管理的?1)enumerateObjectsUsingBlock中的对象 [NSArray array] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { //自动缓存池 } 2)__autoreleasing 修饰的对象 id obj = [NSObject new]; id __autoreleasing o = obj; 3)array、dictiongnary、stringWithString等非init或者new方法生成的对象 int main(int argc, char * argv[]) { NSMutableArray *array = [NSMutableArray array]; NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:5]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; NSMutableString *str = [NSMutableString stringWithString:@"dsdsds"]; 以上类型实验结果: int main(int argc, char * argv[]) { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj; NSMutableArray *array = [NSMutableArray arrayWithCapacity:5]; [array addObject:@"0"]; [array addObject:@"1"]; [array addObject:@"2"]; [array addObject:@"3"]; [array addObject:@"4"]; [array addObject:@"5"]; [array addObject:@"6"]; NSMutableArray *array1 = [NSMutableArray array]; [array1 addObject:@"11"]; [array1 addObject:@"12"]; [array1 addObject:@"13"]; [array1 addObject:@"14"]; [array1 addObject:@"15"]; [array1 addObject:@"16"]; [array1 enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { id __autoreleasing o = obj; }]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"1" forKey:@"1"]; NSMutableString *str = [NSMutableString stringWithString:@"dsdsds"]; // _objc_autoreleasePoolPrint() } //在armv7上、使用_objc_autoreleasePoolPrint()调试打印结果 (lldb) po _objc_autoreleasePoolPrint() objc[96185]: ############## objc[96185]: AUTORELEASE POOLS for thread 0x20d080 objc[96185]: 6 releases pending. objc[96185]: [0x7e115000] ................ PAGE (hot) (cold) objc[96185]: [0x7e115028] 0x7be71ca0 NSObject objc[96185]: [0x7e11502c] 0x7be71ca0 NSObject objc[96185]: [0x7e115030] 0x7c470560 __NSArrayM objc[96185]: [0x7e115034] 0x7be723b0 __NSArrayM objc[96185]: [0x7e115038] 0x7c170b80 __NSDictionaryM objc[96185]: [0x7e11503c] 0x7be72540 __NSCFString objc[96185]: ############## 0x0a5c2500 //在arm64的手机上、使用_objc_autoreleasePoolPrint()调试打印结果 (lldb) po _objc_autoreleasePoolPrint() objc[96400]: ############## objc[96400]: AUTORELEASE POOLS for thread 0x1151d75c0 objc[96400]: 5 releases pending. objc[96400]: [0x7fae43000000] ................ PAGE (hot) (cold) objc[96400]: [0x7fae43000038] 0x600003a6c840 __NSArrayI//系统创建对象 objc[96400]: [0x7fae43000040] 0x600000c358b0 __NSSetI//系统创建对象 objc[96400]: [0x7fae43000048] 0x600002d380d0 NSObject objc[96400]: [0x7fae43000050] 0x600002d380d0 NSObject objc[96400]: [0x7fae43000058] 0x6000021649f0 __NSArrayM objc[96400]: ############## 0xe0675b6edaa1003f (lldb) po 0x6000021649f0 <__NSArrayM 0x600001435d70>( 0, 1, 2, 3, 4, 5, 6 ) 注意:这里面的实验结果不一样,在arm64上、 两个常用的调试命令 //打印自动缓存池对象 _objc_autoreleasePoolPrint() //打印引用计数 _objc_rootRetainCount(obj)
另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,可以添加iOS开发进阶交流群,进群可自行下载! 2、autoreleasePool什么时候创建的,里面的对象又是什么时候释放的?1)系统通过runloop创建的autoreleasePool runloop和autoreleasePool又是什么关系呢?对象又是什么时候释放的? App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。 第二个 Observer 监视了两个事件: 在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。 验证结果: int main(int argc, char * argv[]) { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj; NSMutableArray *array = [NSMutableArray arrayWithCapacity:5]; [array addObject:@"0"]; [array addObject:@"1"]; [array addObject:@"2"]; [array addObject:@"3"]; [array addObject:@"4"]; [array addObject:@"5"]; [array addObject:@"6"]; // _objc_autoreleasePoolPrint() } //_objc_autoreleasePoolPrint调试打印结果 (lldb) po _objc_autoreleasePoolPrint() objc[99121]: ############## objc[99121]: AUTORELEASE POOLS for thread 0x107b0d5c0 objc[99121]: 5 releases pending. objc[99121]: [0x7f93b2002000] ................ PAGE (hot) (cold) objc[99121]: [0x7f93b2002038] 0x6000000d66c0 __NSArrayI objc[99121]: [0x7f93b2002040] 0x6000036b9680 __NSSetI objc[99121]: [0x7f93b2002048] 0x600001780160 NSObject objc[99121]: [0x7f93b2002050] 0x600001780160 NSObject objc[99121]: [0x7f93b2002058] 0x600001bcd230 __NSArrayM objc[99121]: ############## 0x67c4279ea7c20079 (lldb) po 0x600001bcd230 <__NSArrayM 0x600001bcd230>( 0, 1, 2, 3, 4, 5, 6 ) (lldb) po [NSThread currentThread] <NSThread: 0x6000000953c0>{number = 1, name = main} 2)手动autoreleasePool int main(int argc, char * argv[]) { //1\. _objc_autoreleasePoolPrint() @autoreleasepool { id obj = [NSObject new]; id __autoreleasing o = obj; id __autoreleasing o1 = obj; //2\. _objc_autoreleasePoolPrint() } //3\. _objc_autoreleasePoolPrint() } //1\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint() objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c0 objc[1555]: 2 releases pending. 0x2196ee78f1e100fd objc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold) objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: ############## //2\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint() objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c0 0x2196ee78f1e100fd objc[1555]: 5 releases pending. objc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold) objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: [0x7fc2a9802048] ################ POOL 0x7fc2a9802048 objc[1555]: [0x7fc2a9802050] 0x600003afc030 NSObject objc[1555]: [0x7fc2a9802058] 0x600003afc030 NSObject objc[1555]: ############## //3\. _objc_autoreleasePoolPrint() (lldb) po _objc_autoreleasePoolPrint() objc[1555]: ############## objc[1555]: AUTORELEASE POOLS for thread 0x11331a5c0 0x2196ee78f1e100fd objc[1555]: 2 releases pending. objc[1555]: [0x7fc2a9802000] ................ PAGE (hot) (cold) objc[1555]: [0x7fc2a9802038] 0x600002dbb600 __NSArrayI objc[1555]: [0x7fc2a9802040] 0x600001bd8a50 __NSSetI objc[1555]: ############## (lldb) 从上面1、2、3的结果可以看出,当对象出了autoreleasepool的大括号就释放了。 3、子线程的autoreleasepool对象的管理? 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 { //调用 autoreleaseNoPage 方法管理autorelease对象。 return autoreleaseNoPage(obj); } } 三、weak对象内存管理1.释放时机 2.如何实现 weak 的实现原理可以概括以下三步: 1)初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。 代码清单1:示例代码 { id __weak obj1 = obj; } 当我们初始化一个weak变量时,runtime会调用objc_initWeak函数。这个函数在Clang中的声明如下: id objc_initWeak(id *object, id value); 其具体实现如下: id objc_initWeak(id *object, id value) { *object = 0; return objc_storeWeak(object, value); } 示例代码轮换成编译器的模拟代码如下: id obj1; objc_initWeak(&obj1, obj); 因此,这里所做的事是先将obj1初始化为0(nil),然后将obj1的地址及obj作为参数传递给objc_storeWeak函数。 2)添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数。 3)释放时,调用clearDeallocating函数。 四、NSString内存管理1.NSString内存的类型NSString内存分为两种类型:
2.两种内存类型的创建时机。生成一个NSString类型的字符串有三种方法:
NSString *str1 = @"my string";
NSString *str2 = [NSString stringWithString:@"my string"];
NSString *str3 = [[NSString alloc] initWithString:@"my string"]; NSString *str4 = [[NSString alloc]initWithFormat:@"my string"]; 1)对于 NSString *a = @"str"; NSString *b = [[NSString alloc]init]; NSString *c = [[NSString alloc]initWithString:@"str"]; NSString *d = [NSString stringWithString:@"str"]; NSLog(@"%@ : class = %@",a,NSStringFromClass([a class])); NSLog(@"%@ : class = %@",b,NSStringFromClass([b class])); NSLog(@"%@ : class = %@",c,NSStringFromClass([c class])); NSLog(@"%@ : class = %@",d,NSStringFromClass([d class])); //打印结果 2019-06-23 19:23:13.240611+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString 2019-06-23 19:23:13.240764+0800 BlockDemo[47229:789011] : class = __NSCFConstantString 2019-06-23 19:23:13.240870+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString 2019-06-23 19:23:13.240957+0800 BlockDemo[47229:789011] str : class = __NSCFConstantString 2)对于
对于不可以变NSString的测试结果: NSString *e = [[NSString alloc]initWithFormat:@"str"]; NSString *f = [NSString stringWithFormat:@"str"]; NSString *g = [NSString stringWithFormat:@"123456789"]; NSString *h = [NSString stringWithFormat:@"1234567890"]; NSLog(@"%@ : class = %@",e,NSStringFromClass([e class])); NSLog(@"%@ : class = %@",f,NSStringFromClass([f class])); NSLog(@"%@ : class = %@",g,NSStringFromClass([g class])); NSLog(@"%@ : class = %@",h,NSStringFromClass([h class])); //打印结果 2019-06-23 19:27:19.115212+0800 BlockDemo[48129:794364] str : class = NSTaggedPointerString 2019-06-23 19:27:19.115286+0800 BlockDemo[48129:794364] str : class = NSTaggedPointerString 2019-06-23 19:27:19.115388+0800 BlockDemo[48129:794364] 123456789 : class = NSTaggedPointerString 2019-06-23 19:27:19.115476+0800 BlockDemo[48129:794364] 1234567890 : class = __NSCFString 对于可变的NSMutableString NSMutableString *ms1 = [[NSMutableString alloc]init]; NSMutableString *ms2 = [[NSMutableString alloc]initWithString:@"str"]; NSMutableString *ms3 = [[NSMutableString alloc]initWithFormat:@"str"]; NSMutableString *ms4 = [NSMutableString stringWithFormat:@"str"]; NSMutableString *ms5 = [NSMutableString stringWithFormat:@"123456789"]; NSMutableString *ms6 = [NSMutableString stringWithFormat:@"1234567890"]; NSLog(@"%@ : class = %@",ms1,NSStringFromClass([ms1 class])); NSLog(@"%@ : class = %@",ms2,NSStringFromClass([ms2 class])); NSLog(@"%@ : class = %@",ms3,NSStringFromClass([ms3 class])); NSLog(@"%@ : class = %@",ms4,NSStringFromClass([ms4 class])); NSLog(@"%@ : class = %@",ms5,NSStringFromClass([ms5 class])); NSLog(@"%@ : class = %@",ms6,NSStringFromClass([ms6 class])); //打印结果 2019-06-23 19:34:08.521931+0800 BlockDemo[49465:802590] : class = __NSCFString 2019-06-23 19:34:08.522058+0800 BlockDemo[49465:802590] str : class = __NSCFString 2019-06-23 19:34:08.522131+0800 BlockDemo[49465:802590] str : class = __NSCFString 2019-06-23 19:34:08.522196+0800 BlockDemo[49465:802590] str : class = __NSCFString 2019-06-23 19:34:08.522281+0800 BlockDemo[49465:802590] 123456789 : class = __NSCFString 2019-06-23 19:34:08.522372+0800 BlockDemo[49465:802590] 1234567890 : class = __NSCFString 从结果我们可以看出来NSMutableString都是分配在堆区,且是__NSCFString类型,NSString中Format相关方法也是都分配在堆区,但是会根据字符串的长度,区分为__NSCFString和NSTaggedPointerString两种。在分配堆区的这些变量,其实一部分是正常的对象,一部分变成autorelease对象,具体是哪些,我们可以使用_objc_autoreleasePoolPrint()打印出来,比如实例中的g、ms4、ms5、ms6。 参考: |
|
来自: python_lover > 《待分类》