这篇文章简单的根据几个常见的面试题,引导出了iOS在ARC模式下仍需要注意的一些内存管理方面的小知识,本文中提到的一些知识点需要仔细的考虑。
1、存储空间的类别和管理方式
1 2 3 4 5 6 7 8 9 10 11
| 1、栈区(stack)——由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap)——一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static)——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区——常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区——存放函数体的二进制代码。
|
1、NSString *str = @”111”
首先str是一个指针,这种指针变量本身肯定是在栈空间,而@”111”是一个常量
字符串,注意iOS中NSString所指向的NSString是一个常量,分配在常量区,
你可能会问,常量存储区不是不能改变吗?我们可以验证下
1 2 3 4 5 6
| NSString *str1 = @"121"; NSLog(@"%p",str1); // 两个打印结果相同,可以证明str1指向一个常量 NSLog(@"%p",@"121"); // 而通过subString 等方法返回的是一个新的常量,NSNumber同理
|
关于NSString的分析 可以参考这篇文章
3、OC和C在数组表示中的区别
2、使用copy和strong修饰数组的区别
代码:
@property (nonatomic, strong) NSString *str1;
@property (nonatomic, copy) NSString *str2;
结果:
3、自动释放池的使用
附赠
:
1 2 3 4 5 6 7 8
| 测试代码耗时的方法: NSLog(@"开始"); CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); [self answer1]; CFAbsoluteTime end = CFAbsoluteTimeGetCurrent(); NSLog(@"外部 %f", end - start);
|
2、const关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const char *p; char const *p; char * const p; const char * const p; 参考答案:
const char *p定义了一个指向不可变的字符串的字符指针,可以这么看:const char *为类型,p是变量。 char const *p与上一个是一样的。 char * const p定义了一个指向字符串的指针,该指针值不可改变,即不可改变指向。这么看:char *是类型,const是修饰变量p,也就是说p是一个常量 const char * const p定义了一个指向不可变的字符串的字符指针,且该指针也不可改变指向。这一个就很容易看出来了。两个const分别修饰,因此都是不可变的。
int age = 10; int money = 200;
// const修饰的是*p1 int const * p1 = &age;
// const修饰的是p2 int * const p2 = &age;
*p1 = 999; // 错误 p1 = &money; // 正确 *p2 = 999; // 正确 p2 = &money; // 错误
// OC字符串常量的定义 NSString * const url = @"http://baidu.com"; url = @"http://apple.com"; // 错误
|
4、static 关键字
1 2 3 4 5 6 7 8 9 10 11
| static的作用 -修饰`局部变量` :修改的是`生命周期`
1> 被static修饰的局部变量,在整个程序运行过程中,都只有一份内存 2> 被static修饰的局部变量,并没有改变作用域
修饰`全局变量` : 修改的是`作用域`
1> 没有被static修饰的全局变量,能被项目中的任何文件访问 2> 被static修饰的全局变量,只能被本文件访问(定义这个变量的文件)
|
3、iOS内存管理(来自标哥)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 描述一下iOS的内存管理,在开发中对于内存的使用和优化包含哪些方面。我们在开发中应该注意哪些问题。
内存管理准则:谁强引用过,谁就在不再使用时使引用计数减一。
对于内存的使用和优化常见的有以下方面:
重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用。
尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能。
不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多。
选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。
gzip/zip压缩:当从服务端下载相关附件时,可以通过gzip/zip压缩后再下载,使得内存更小,下载速度也更快。
延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉 重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要。
使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存。
正确选择图片加载方式:详情阅读细读UIImage加载方式
|
4、数组名称与数组首元素地址
1 2 3 4 5 6
| int array[3] = {11, 22, 33}; // array可看做是一个指针:指向array[0](某个数组元素),指向是int类型的数据(4个字节的数据) // &array[0]可看做是一个指针:指向array[0](某个数组元素),指向是int类型的数据(4个字节的数据) // &array可看做是一个指针:指向array(整个数组),指向int[3]类型的数据(12个字节的数据)
// &array[0] === array
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int array[2][3] = { {1, 11, 111}, {2, 22, 222} }; // array[0] : 指向array[0][0]元素(4个字节的空间) // array[1] : 指向array[1][0]元素(4个字节的空间) // &array[0][0] : 指向array[0][0]元素(4个字节的空间) // &array[1][0] : 指向array[1][0]元素(4个字节的空间) // array : 指向array[0](数组{1, 11, 111},12个字节的空间) // &array : 指向array(整个数组,24个字节的空间)
// array[0] === &array[0][0] // array[1] === &array[1][0] // array === &array[0]
|