2019-03-27 面試題整理

1. 屬性修飾符

經常使用的屬性修飾符有前端

atomic,nonatomic,strong,retain,weak,assign,unsafe_unretained,copy,readonly,readwritegit

2. ARC下,不指定屬性修飾符時,默認的是

  1. 基本數據類型:atomic readwrite assign
  2. 普通OC對象:atomic readwrite strong

3. 關於copystrong

可變對象 copy是深拷貝程序員

不可變對象 copy是淺拷貝github

mutableCopy 始終是深拷貝數組

3.1 爲何要用copy修飾NSString/NSArray/NSDictory

由於使用copy來修飾不可變對象,能夠保證安全安全

擴展:async

copy淺拷貝 不拷貝對象自己,僅僅是拷貝指向對象的指針(複製的對象和原對象都指向同一個地址)函數

mutableCopy深拷貝 直接拷貝整個對象內存到另外一塊內存中ui

3.2 使用copy去修飾NSMutableArray會怎麼樣?

使用copy修飾可變數組以後,數組初始化的時候,會執行copy方法,生成的是一個不可變的數組,當執行[arr addObject:]時會crashatom

4. atomic是不是絕對線性安全的

atomic原子性,不是絕對線性安全的

@property (atomic, assign) NSInteger intA;   //有一個atomic的屬性,表示是原子的
 
 
- (void)viewDidLoad {
   [super viewDidLoad];
   //開啓一個線程對intA的值+1
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       for (int i = 0;i < 1000;i ++){
           self.intA = self.intA + 1;
       }
       NSLog(@"intA : %ld",(long)self.intA);
   });
   
   //開啓一個線程對intA的值+1
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       for (int i = 0;i < 1000;i ++){
           self.intA = self.intA + 1;
       }
       NSLog(@"intA : %ld",(long)self.intA);
   });   
}

錯誤分析:
由於intAatomic修飾的,因此是線程安全的,在+1的時候,只會有一個線程去操做,因此最終的打印結果一定有一個是2000

輸出以下:

intA : 1186
intA : 896

分析:
其實atomic是原子的是沒問題的,這個只是表示set方法是原子的,效果相似與下面的效果

//atomic表示的是對set方法加鎖,表示在設置值的時候,只會有一個線程執行set方法
- (void)setIntA:(NSInteger)intA{
    [self.lock lock];
    _intA = intA;
    [self.lock unlock];
}

只是對set方法加鎖,而咱們代碼裏面的self.intA = self.intA + 1;,這一部分不是線程安全的,正確的處理方法是:

[self.lock lock];
self.intA = self.intA + 1;
[self.lock unlock];

5. 進程與線程,堆和棧

一個程序至少有一個進程,一個進程至少有一個線程。同一個進程內的線程共享進程裏的資源。

堆 由程序員分配釋放,通常用來存放對象(ARC下會自動釋放)
棧 由編譯器自動分配釋放,存放函數的參數值、局部變量的值等

6. UIButton繼承自什麼?爲何?

UIButton是一個能夠響應事件的控件,所以它的直接父類是UIControlUIControl的直接父類是UIView
UIButton從父類UIControl那繼承了控制相關的方法,好比添加事件、移除事件等

7. 響應鏈與事件傳遞

https://raw.githubusercontent.com/WuOtto/imgSrc/master/iOS_DefaultResponderChain.png

UIResponder響應者對象,只要繼承自UIResponder的類,才能處理事件。

UIApplicationUIViewUIViewController都是繼承自UIResponder類,能夠響應和處理事件。CALayer不是UIResponder的子類,沒法處理事件。

事件的分發與傳遞:

  1. 當iOS程序中發生觸摸事件後,系統會將事件加入到UIApplication管理的一個任務隊列中
  2. UIApplication將處於任務隊列最前端的事件向下分發。即UIWindow。
  3. UIWindow將事件向下分發,即UIView。
  4. UIView首先看本身是否能處理事件,觸摸點是否在本身身上。若是能,那麼繼續尋找子視圖。
  5. 遍歷子控件,重複以上兩步。
  6. 若是沒有找到,那麼本身就是事件處理者。若是
  7. 若是本身不能處理,那麼不作任何處理。
    其中UIView不接受事件處理的狀況主要有如下三種
1. alpha <0.01
2. userInteractionEnabled = NO
3. hidden = YES.

響應者鏈:

響應鏈是從最合適的view開始傳遞,處理事件傳遞給下一個響應者,響應者鏈的傳遞方法是事件傳遞的反方法,若是全部響應者都不處理事件,則事件被丟棄。咱們一般用響應者鏈來獲取上幾級響應者,方法是UIRespondernextResponder方法。

8. 請寫出下面這段代碼的輸出

NSString *str1 = [NSString stringWithFormat:@"hello"];
NSString *str2 = @"hello";
NSString *str3 = @"hello";
    
if (str1 == str2) {
    NSLog(@"str1 = str2");
}
    
if (str2 == str3) {
    NSLog(@"str2 = str3");
}
    
if ([str1 isEqualToString:str2]) {
    NSLog(@"str1 isEqualToString:str2");
}

輸出以下:

str2 = str3
str1 isEqualToString:str2

這裏考察的是常量池相關的知識點
isEqualToString比較的是兩個字符串的內容;
==比較的是地址的引用;
這裏str2 == str3返回true,主要是與常量池有關;在給str2賦值的時候,將hello一塊兒放入了常量池中,當再次將hello賦值給str3的時候,先從常量池中查看是否存在hello的值,若是有,則直接取出。因此str2和str3指的是同一個引用,所以返回的結果天然是true

擴展: iOS程序中的內存分爲:堆區、棧區、全局區(靜態區)、常量區、方法區

相關文章
相關標籤/搜索