面试题答案
一键面试复合赋值运算符底层实现
在Objective - C中,复合赋值运算符(如+=
、-=
等)本质上是对基本赋值运算符和算术运算符的组合优化。以+=
为例,a += b
在底层大致等价于a = a + b
。
当编译器处理这些复合赋值运算符时,会根据操作数的类型生成相应的机器码。对于基本数据类型(如int
、float
等),编译器会生成对应的算术运算指令(如ADD
、SUB
等CPU指令)以及赋值指令。对于对象类型(如NSNumber
等),如果重载了对应的运算符,会调用对象的相应方法来完成运算和赋值操作。
性能敏感循环中提升性能的方法
- 减少内存访问次数:
- 如果循环中的变量是局部变量且在栈上,访问速度相对较快。尽量避免在循环中频繁访问全局变量或堆上的数据,因为访问这些数据需要更多的内存寻址操作。例如:
这里int sum = 0; for (int i = 0; i < 1000000; i++) { sum += i; }
sum
和i
都是局部变量,在栈上分配,访问速度快。 - 利用CPU指令级并行:
- 现代CPU支持单指令多数据(SIMD)指令集,如ARM架构的NEON指令集,x86架构的SSE指令集等。如果循环中的计算可以向量化,编译器可能会自动利用这些指令集进行优化。例如,对于处理数组的循环:
编译器可能会将其优化为使用SIMD指令并行处理多个数组元素,从而提升性能。float arr[1000]; for (int i = 0; i < 1000; i++) { arr[i] += 1.0f; }
- 减少不必要的计算:
- 在循环中,如果部分计算结果在每次迭代中不会改变,可以将其提取到循环外部。例如:
这里int factor = 2; for (int i = 0; i < 1000; i++) { int result = i * factor; // 其他操作 }
factor
在循环中不变,提取到循环外避免了每次迭代都重新计算。 - 使用合适的数据类型:
- 如果循环中的计算不需要高精度,可以使用占用内存小的数据类型,如
int8_t
、int16_t
等(前提是不会导致数据溢出)。这样可以减少内存带宽的占用,提高缓存命中率,从而提升性能。例如,在处理一些表示状态的小整数时:
int8_t status = 0; for (int i = 0; i < 1000; i++) { status += 1; }
- 如果循环中的计算不需要高精度,可以使用占用内存小的数据类型,如
- 开启编译器优化选项:
- 在编译时,开启适当的优化选项。例如在Xcode中,可以在Build Settings里设置优化级别为
-O3
(最高优化级别)。编译器会进行一系列优化,如循环展开、死代码消除等,从而提升性能。例如,循环展开可以减少循环控制的开销:
编译器在int sum = 0; for (int i = 0; i < 1000; i += 4) { sum += i; sum += i + 1; sum += i + 2; sum += i + 3; }
-O3
优化级别下可能会自动进行类似的循环展开优化。 - 在编译时,开启适当的优化选项。例如在Xcode中,可以在Build Settings里设置优化级别为