iOS 面试题

2019-12-10  本文已影响0人  Jimmy_N9

问题:

  1. 请说一下栈区与堆区的区别?你所知道的分区还有哪些?

  2. 分别说你对strongcopy, assignweak的理解。并说出其两两的区别。

  3. 这个写法有什么问题?@property (copy) NSMutableArray* array;

  4. @synthesize与@dynamic有什么作用?

  5. 如何在categoryprotocol中添加属性?

  - (id)init
  {
      self = [super init];
      if (self) {
          NSLog(@"%@", NSStringFromClass([self class]));
          NSLog(@"%@", NSStringFromClass([super class]));
      }
      return self;
  }
  @end

阅读以上代码,会输出什么?
7.使用block时什么情况会发生循环引用?如何解决?

  1. OC 中的有哪些方法实现多线程?请说一下其区别
  2. 如何使用GCD同步若干个异步调用?(如加载多张图片,然后合成一张图)
  3. 阅读以下代码:
    (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)主线程下运行会出现什么情况?

  1. Runloop 的 mode的作用是什么?
  2. 使用NSTimer时该注意些什么?
  3. 你使用runtime 实现过些什么?
  4. iOS的数据持久化有哪些?
  5. 判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。

答案:

(1)栈区的地址从高到低分配,堆区是由低到高分配
(2)栈区的内存分配是连续的,堆区的内存分配是不连续的
(3)栈区的内存操作效率高,由于堆区分配的内存结构比较复杂,所以操作起来效率比栈区低。
其他分区:
全局区、常量区、代码区。

2 :
strongcopy都会使引用计数加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 告诉编译器:属性的 settergetter方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为@dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺 getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

5 :
protocol中使用 property只会生成 settergetter方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
category 使用 @property也是只会生成 settergetter 方法的声明,如果我们真的需要给 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;
}
上一篇下一篇

猜你喜欢

热点阅读