iOS 面试题
问题:
-
请说一下栈区与堆区的区别?你所知道的分区还有哪些?
-
分别说你对
strong
与copy
,assign
与weak
的理解。并说出其两两的区别。 -
这个写法有什么问题?
@property (copy) NSMutableArray* array;
-
@synthesize
与@dynamic
有什么作用? -
如何在
category
与protocol
中添加属性?
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
阅读以上代码,会输出什么?
7.使用block时什么情况会发生循环引用?如何解决?
- OC 中的有哪些方法实现多线程?请说一下其区别
- 如何使用GCD同步若干个异步调用?(如加载多张图片,然后合成一张图)
- 阅读以下代码:
(1):
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
});
NSLog(@"3");
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"4");
});
NSLog(@“5”);
(2):
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"test");
});
(1)请写打印的顺序:
(2)主线程下运行会出现什么情况?
- Runloop 的 mode的作用是什么?
- 使用NSTimer时该注意些什么?
- 你使用runtime 实现过些什么?
- iOS的数据持久化有哪些?
- 判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。
答案:
(1)栈区的地址从高到低分配,堆区是由低到高分配
(2)栈区的内存分配是连续的,堆区的内存分配是不连续的
(3)栈区的内存操作效率高,由于堆区分配的内存结构比较复杂,所以操作起来效率比栈区低。
其他分区:
全局区、常量区、代码区。
2 :
strong
与copy
都会使引用计数加1,但strong
是两个指针指向同一个内存地址,copy
会在内存里拷贝一份对象,两个指针指向不同的内存地址,以及copy
修饰可变对象时要额外注意。
assign
适用于基本数据类型,weak
是适用于NSObject
对象,并且是一个弱引用。
weak
修饰的对象在释放之后,指针地址会被置为nil
。所以现在一般弱引用就是weak
。
assign
其实也可以用来修饰对象,那么我们为什么不用它修饰对象呢?因为被assign
修饰的对象(一般编译的时候会产生警告:Assigning retained object to unsafe property; object will be released after assignment
)在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil,造成野指针。对象一般分配在堆上的某块内存,如果早后续的内存分配中,刚好分配到这块地址,程序就会崩掉。
那为什么可以用assign
修饰基本数据类型?因为基础数据类型一般分配在栈区,栈区的内存会由系统自动处理,不会造成野指针
3 :
1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy
就是复制一个不可变 NSArray
的对象;
2、使用了 atomic
属性会严重影响性能
4 :
@property
有两个对应的词,一个是 @synthesize
,一个是 @dynamic
。如果 @synthesize
和 @dynamic
都没写,那么默认的就是@syntheszie var = _var;
@synthesize
的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
@dynamic
告诉编译器:属性的 setter
与 getter
方法由用户自己实现,不自动生成。(当然对于 readonly
的属性只需提供 getter
即可)。假如一个属性被声明为@dynamic var
,然后你没有提供 @setter
方法和 @getter
方法,编译的时候没问题,但是当程序运行到instance.var = someVar
,由于缺 setter
方法会导致程序崩溃;或者当运行到 someVar = var
时,由于缺 getter
方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
5 :
在protocol
中使用 property
只会生成 setter
和 getter
方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
category
使用 @property
也是只会生成 setter
和 getter
方法的声明,如果我们真的需要给 category
增加属性的实现,需要借助于运行时的两个函数:
objc_setAssociatedObject
objc_getAssociatedObject
6 :都输出 Son
7 :一个对象中强引用了block,在block中又强引用了该对象,就会发射循环引用。
解决方法是将该对象使用__weak或者__block修饰符修饰之后再在block中使用。
id __weak weakSelf = self;
或者 __weak typeof(&*self)weakSelf = self
该方法可以设置宏
id __block weakSelf = self;
或者将其中一方强制制空 xxx = nil
。
8 :
1、 NSThread
2、Cocoa NSOperation (使用NSOperation和NSOperationQueue)
3、GCD(Grand Central Dispatch)
4、 pthread
9 :
使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并图片
});
10: (1)1、4、5、3、2
(2)Crash
11: model 主要是用来指定事件在运行循环中的优先级的,分为:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
:默认,空闲状态
UITrackingRunLoopMode
:ScrollView滑动时
UIInitializationRunLoopMode
:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes)
:Mode集合
苹果公开提供的 Mode 有两个:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
12: 1. 循环引用问题 2. 在滑动时注意 Runloop 的mode问题
13: 开放性问题,动态添加类,属性,方法和方法交换等等。
14: plist ,归档,userdefault , coredata , sqlite 等等
15:
如果没有空间复杂度限制,用哈希表实现。 时间和空间复杂度都是O(N)。
如限制空间复杂度,则应先排序,后判断,排好序后的重复元素就对相邻,方便判断。
考察经典排序算法,空间复杂度限制。O(1)的是堆排序,当不能使用递归的实现方式,因为递归法下堆排序的空间复杂度为O(logN),函数栈深度为logN,所以应该改为非递归版本的堆排序。
//堆来做
#include<iostream>
#include<vector>
using namespace std;
void swap(int*a,int *b){
int tmp=*a;
*a=*b;
*b=tmp;
}
void heapSort(vector<int>&a,int n){
for(int i=0;i<n;i++){
//对n-i调整大顶堆
for(int j=n-i-1;j>0;j--){
int pre=(j-1)/2;
if(j%2==0){
pre=(j-2)/2;
}
if(a[j]>a[pre]){
swap(&a[j],&a[pre]);
}
}
swap(&a[0],&a[n-1-i]);
}
}
bool checkDuplicate(vector<int> a, int n) {
// write code here
heapSort(a,n);
for(int i=1;i<n;i++){
if(a[i]==a[i-1]){
return true;
}
}
return false;
}
int main(){
int v[]={4,6,1,3,5,6,2};
//int v[]={1,1};
vector<int> a(v,v+sizeof(v)/sizeof(int));
cout<<checkDuplicate(a,a.size())<<endl;
return 0;
}