一篇很全面的IOS面試題(下)

是否知否發表於2019-04-18
  1. 如何實現檢視的變形?

    答:通過修改view的 transform 屬性即可。

  2. 在手勢物件基礎類UIGestureRecognizer的常用子類手勢型別中哪兩個手勢發生後,響應只會執行一次?

    答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手勢,手勢發生後,響應只會執行一次。

  3. 字串常用方法:

    NSString str = @"abc123"; NSArray arr = [str componentsSeparatedByString:@""]; //以目標字串把原字串分割成兩部分,存到陣列中。@[@"abc", @"123"];

  4. 如何高效能的給 UIImageView 加個圓角?

*   不好的解決方案:使用下面的方式會`強制Core Animation提前渲染螢幕的離屏繪製, 而離屏繪製就會給效能帶來負面影響`,會有卡頓的現象出現。 self.view.layer.cornerRadius = 5.0f; self.view.layer.masksToBounds = YES; 

    *   正確的解決方案:使用繪圖技術

        - (UIImage *)circleImage { // NO代表透明 UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); // 獲得上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 新增一個圓 CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); CGContextAddEllipseInRect(ctx, rect); // 裁剪 CGContextClip(ctx); // 將圖片畫上去 [self drawInRect:rect]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 關閉上下文 UIGraphicsEndImageContext(); return image;
        } 

    *   還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 新增了的圓角,其實也是通過繪圖技術來實現的。 UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        imageView.center = CGPointMake(200, 300); UIImage *anotherImage = [UIImage imageNamed:@"image"]; UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
        [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
                               cornerRadius:50] addClip];
        [anotherImage drawInRect:imageView.bounds];
        imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
        [self.view addSubview:imageView];
複製程式碼
  1. 你是怎麼封裝一個view的
1). 可以通過純程式碼或者xib的方式來封裝子控制元件 2). 建立一個跟view相關的模型,然後將模型資料傳給view,通過模型上的資料給view的子控制元件賦值 /**
     *  純程式碼初始化控制元件時一定會走這個方法
     */ - (instancetype)initWithFrame:(CGRect)frame { if(self = [super initWithFrame:frame]) {
            [self setupUI];
        } return self;
    } /**
     *  通過xib初始化控制元件時一定會走這個方法
     */ - (id)initWithCoder:(NSCoder *)aDecoder { if(self = [super initWithCoder:aDecoder]) {
            [self setupUI];
        } return self;
    }

    - (void)setupUI { // 初始化程式碼 }
複製程式碼
  1. HTTP協議中 POST 方法和 GET 方法有那些區別?

    1. GET用於向伺服器請求資料,POST用於提交資料 2. GET請求,請求引數拼接形式暴露在位址列,而POST請求引數則放在請求體裡面,因此GET請求不適合用於驗證密碼等操作 3. GET請求的URL有長度限制,POST請求不會有長度限制

  2. 請簡單的介紹下APNS傳送系統訊息的機制

    APNS優勢:杜絕了類似安卓那種為了接受通知不停在後臺喚醒程式保持長連線的行為,由iOS系統和APNS進行長連線替代。 APNS的原理: 1). 應用在通知中心註冊,由iOS系統向APNS請求返回裝置令牌(device Token) 2). 應用程式接收到裝置令牌併傳送給自己的後臺伺服器 3). 伺服器把要推送的內容和裝置傳送給APNS 4). APNS根據裝置令牌找到裝置,再由iOS根據APPID把推送內容展示

77. ios開發逆向傳值的幾種方法整理

第一種:代理傳值

第二個控制器: @protocol WJSecondViewControllerDelegate  - (void)changeText:(NSString*)text; @end @property(nonatomic,assign)iddelegate;

  - (IBAction)buttonClick:(UIButton*)sender {
  _str = sender.titleLabel.text;
  [self.delegate changeText:sender.titleLabel.text];
  [self.navigationController popViewControllerAnimated:YES];
  }

  第一個控制器:

  - (IBAction)pushToSecond:(id)sender {
  WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];
  svc.delegate = self;
  svc.str = self.navigationItem.title;
  [self.navigationController pushViewController:svc animated:YES];
  [svc release];
  }
  - (void)changeText:(NSString *)text{ self.navigationItem.title = text;
  }
複製程式碼

第二種:通知傳值

第一個控制器: //註冊監聽通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@"NOV" object:nil];
  - (void)limitDataForModel:(NSNotification *)noti{ self.gamesInfoArray = noti.object;
  }

  第二個控制器: //傳送通知 [[NSNotificationCenter defaultCenter]     postNotificationName:@"NOV" object:gameArray];
複製程式碼

第三種:單例傳值

Single是一個單例類,並且有一個字串型別的屬性titleName
  在第二個控制器:

  - (IBAction)buttonClick:(UIButton*)sender {
  Single *single = [Single sharedSingle];
  single.titleName = sender.titleLabel.text;
  [self.navigationController popViewControllerAnimated:YES];
  }

  第一個控制器:

  - (void)viewWillAppear:(BOOL)animated{
  [super viewWillAppear:animated];
  Single *single = [Single sharedSingle]; self.navigationItem.title = single.titleName;
  }
複製程式碼

第四種:block傳值

第二個控制器: @property (nonatomic,copy) void (^changeText_block)(NSString*);
  - (IBAction)buttonClick:(UIButton*)sender {
  _str = sender.titleLabel.text; self.changeText_block(sender.titleLabel.text);
  [self.navigationController popViewControllerAnimated:YES];
  }

  第一個控制器:

  - (IBAction)pushToSecond:(id)sender {
  WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];
  svc.str = self.navigationItem.title;
  [svc setChangeText_block:^(NSString *str) {
      >self.navigationItem.title = str;
  }];
  [self.navigationController pushViewController:svc animated:YES];
  }
複製程式碼

第五種:extern傳值

第二個控制器: extern NSString *btn;
  - (IBAction)buttonClick:(UIButton*)sender {
  btn = sender.titleLabel.text;
  [self.navigationController popViewControllerAnimated:YES];
  }

  第一個控制器: NSString *btn = nil;
  - (void)viewWillAppear:(BOOL)animated{
  [super viewWillAppear:animated]; self.navigationItem.title = btn;
  }
複製程式碼

第六種:KVO傳值

第一個控制器:

  - (void)viewDidLoad {
  [super viewDidLoad];
   _vc =[[SecondViewController alloc]init]; //self監聽vc裡的textValue屬性 [_vc addObserver:self forKeyPath:@"textValue" options:0 context:nil];   
  }

  第二個控制器:

  - (IBAction)buttonClicked:(id)sender { self.textValue = self.textField.text;
  [self.navigationController popViewControllerAnimated:YES];
  }
複製程式碼

78. 淺談iOS開發中方法延遲執行的幾種方式

Method1. performSelector方法

Method2. NSTimer定時器

Method3. NSThread執行緒的sleep

Method4. GCD


公用延遲執行方法

  • (void)delayMethod{ NSLog(@"delayMethodEnd");

Method1: performSelector

[self performSelector:@selector(delayMethod) withObject:nil/*可傳任意型別引數*/ afterDelay:2.0];`注:此方法是一種非阻塞的執行方式,未找到取消執行的方法。 > 程式執行結束> 2015-08-31 10:56:59.361 CJDelayMethod[1080:39604] delayMethodStart2015-08-31 10:56:59.363 CJDelayMethod[1080:39604] nextMethod2015-08-31 10:57:01.364 CJDelayMethod[1080:39604] delayMethodEnd
複製程式碼

Method2: NSTimer定時器

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];`
  注:此方法是一種非阻塞的執行方式,
  取消執行方法:`- (void)invalidate;`即可

  > 程式執行結束
  > 2015-08-31 10:58:10.182 CJDelayMethod[1129:41106] delayMethodStart2015-08-31 10:58:10.183 CJDelayMethod[1129:41106] nextMethod2015-08-31 10:58:12.185 CJDelayMethod[1129:41106] delayMethodEnd
複製程式碼

Method3: NSThread執行緒的sleep

[NSThread sleepForTimeInterval:2.0];  注:此方法是一種阻塞執行方式,建議放在子執行緒中執行,否則會卡住介面。但有時還是需要阻塞執行,如進入歡迎介面需要沉睡3秒才進入主介面時。  沒有找到取消執行方式。   > 程式執行結束  > 2015-08-31 10:58:41.501 CJDelayMethod[1153:41698] delayMethodStart2015-08-31 10:58:43.507 CJDelayMethod[1153:41698] nextMethod
複製程式碼

Method4: GCD

__block ViewController/*主控制器*/ *weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延遲執行時間*/ * NSEC_PER_SEC));   dispatch_after(delayTime, dispatch_get_main_queue(), ^{      [weakSelf delayMethod];  });   注:此方法可以在引數中選擇執行的執行緒,是一種非阻塞執行方式。沒有找到取消執行方式。   > 程式執行結束  > 2015-08-31 10:59:21.652 CJDelayMethod[1181:42438] delayMethodStart2015-08-31 10:59:21.653 CJDelayMethod[1181:42438] nextMethod2015-08-31 10:59:23.653 CJDelayMethod[1181:42438] delayMethodEnd
複製程式碼
完整程式碼參見:

  > // > // ViewController.m > // CJDelayMethod > // > // Created by 陳杰 on 8/31/15. > // Copyright (c) 2015 chenjie. All rights reserved. > // > 
  > # import "ViewController.h" > 
  > @interface ViewController () > @property (nonatomic, strong) NSTimer *timer;
  > @end > @implementation ViewController* > 
  >  *`- (void)viewDidLoad { `*
  > 
  > *` [super viewDidLoad]; `*
  > 
  > *` NSLog(@"delayMethodStart"); `*
  > 
  > *` [self methodOnePerformSelector];// `* > 
  > *` [self methodTwoNSTimer];// `* > 
  > *` [self methodThreeSleep];//`* > 
  > *` [self methodFourGCD]; `*
  > 
  > *` NSLog(@"nextMethod");`*
  > 
  > *`}`*
  > 
  >  *`- (void)methodFiveAnimation{ `*
  > 
  > *` [UIView animateWithDuration:0 delay:2.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ } completion:^(BOOL finished) { `*
  > 
  > *` [self delayMethod]; `*
  > 
  > *` }];`*
  > 
  > *`}`
  > `- (void)methodFourGCD{ `*
  > 
  > *` __block ViewController`*`weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ `
  > 
  > ` [weakSelf delayMethod]; `
  > 
  > ` });`
  > 
  > `}`
  > 
  >  `- (void)methodThreeSleep{ `
  > 
  > ` [NSThread sleepForTimeInterval:2.0];`
  > 
  > `}`
  > 
  >  `- (void)methodTwoNSTimer{`
  > 
  > ` NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];`
  > 
  > `}`
  > 
  >  `- (void)methodOnePerformSelector{`
  > 
  > ` [self performSelector:@selector(delayMethod) withObject:nil/*可傳任意型別引數*/ afterDelay:2.0];`
  > 
  > `}`
  > 
  >  `- (void)delayMethod{`
  > 
  > ` NSLog(@"delayMethodEnd");`
  > 
  > `}`
  > `- (void)didReceiveMemoryWarning { `
  > 
  > ` [super didReceiveMemoryWarning]; `
  > 
  > ` // Dispose of any resources that can be recreated.` > 
  > `}`
  > 
  > `@end`
複製程式碼

  1. NSPersistentStoreCoordinator , NSManaged0bjectContext 和NSManaged0bject中的那些需要線上程中建立或者傳遞

    答:NSPersistentStoreCoordinator是持久化儲存協調者,主要用於協調託管物件上下文和持久化儲存區之間的關係。NSManagedObjectContext使用協調者的託管物件模型將資料儲存到資料庫,或查詢資料。

  2. 您是否做過一部的網路處理和通訊方面的工作?如果有,能具體介紹一下實現策略麼?

    答:使用NSOperation傳送非同步網路請求,使用NSOperationQueue管理執行緒數目及優先順序,底層是用NSURLConnetion,

  3. 你使用過Objective-C的執行時程式設計(Runtime Programming)麼?如果使用過,你用它做了什麼?你還能記得你所使用的相關的標頭檔案或者某些方法的名稱嗎?

    答:Objecitve-C的重要特性是Runtime(執行時),在#import 下能看到相關的方法,用過objc_getClass()和class_copyMethodList()獲取過私有API;使用 Method method1 = class_getInstanceMethod(cls, sel1); Method method2 = class_getInstanceMethod(cls, sel2); method_exchangeImplementations(method1, method2);   程式碼交換兩個方法,在寫unit test時使用到。

  4. Core開頭的系列的內容。是否使用過CoreAnimation和CoreGraphics。UI框架和CA,CG框架的聯絡是什麼?分別用CA和CG做過些什麼動畫或者影像上的內容。(有需要的話還可以涉及Quartz的一些內容)

    答:UI框架的底層有CoreAnimation,CoreAnimation的底層有CoreGraphics。

    UIKit
    Core Animation
    Core Graphics
    Graphics Hardware

    使用CA做過menu選單的展開收起(太遜了)

  5. 是否使用過CoreText或者CoreImage等?如果使用過,請談談你使用CoreText或者CoreImage的體驗。

    答:CoreText可以解決複雜文字內容排版問題。CoreImage可以處理圖片,為其新增各種效果。體驗是很強大,挺複雜的。

  6. 自動釋放池是什麼,如何工作

    答:當您向一個物件傳送一個autorelease訊息時,Cocoa就會將該物件的一個引用放入到最新的自動釋放.它仍然是個OC的物件,因此自動釋放池定義的作用域內的其它物件可以向它傳送訊息。當程式執行到作用域結束的位置時,自動釋放池就會被釋放,池中的所有物件也就被釋放。

  7. NSNotification和KVO的區別和用法是什麼?什麼時候應該使用通知,什麼時候應該使用KVO,它們的實現上有什麼區別嗎?如果用protocol和delegate(或者delegate的Array)來實現類似的功能可能嗎?如果可能,會有什麼潛在的問題?如果不能,為什麼?(雖然protocol和delegate這種東西面試已經面爛了…)

    答:NSNotification是通知模式在iOS的實現,KVO的全稱是鍵值觀察(Key-value observing),其是基於KVC(key-value coding)的,KVC是一個通過屬性名訪問屬性變數的機制。例如將Module層的變化,通知到多個Controller物件時,可以使用NSNotification;如果是隻需要觀察某個物件的某個屬性,可以使用KVO。 對於委託模式,在設計模式中是物件介面卡模式,其是delegate是指向某個物件的,這是一對一的關係,而在通知模式中,往往是一對多的關係。委託模式,從技術上可以現在改變delegate指向的物件,但不建議這樣做,會讓人迷惑,如果一個delegate物件不斷改變,指向不同的物件。

  8. 你用過NSOperationQueue麼?如果用過或者瞭解的話,你為什麼要使用NSOperationQueue,實現了什麼?請描述它和G.C.D的區別和類似的地方(提示:可以從兩者的實現機制和適用範圍來描述)。

    答:使用NSOperationQueue用來管理子類化的NSOperation物件,控制其執行緒併發數目。GCD和NSOperation都可以實現對執行緒的管理,區別是 NSOperation和NSOperationQueue是多執行緒的物件導向抽象。專案中使用NSOperation的優點是NSOperation是對執行緒的高度抽象,在專案中使用它,會使專案的程式結構更好,子類化NSOperation的設計思路,是具有物件導向的優點(複用、封裝),使得實現是多執行緒支援,而介面簡單,建議在複雜專案中使用。 專案中使用GCD的優點是GCD本身非常簡單、易用,對於不復雜的多執行緒操作,會節省程式碼量,而Block引數的使用,會是程式碼更為易讀,建議在簡單專案中使用。

  9. 既然提到G.C.D,那麼問一下在使用G.C.D以及block時要注意些什麼?它們兩是一回事兒麼?block在ARC中和傳統的MRC中的行為和用法有沒有什麼區別,需要注意些什麼?

    答:使用block是要注意,若將block做函式引數時,需要把它放到最後,GCD是Grand Central Dispatch,是一個對執行緒開源類庫,而Block是閉包,是能夠讀取其他函式內部變數的函式。

  10. 對於Objective-C,你認為它最大的優點和最大的不足是什麼?對於不足之處,現在有沒有可用的方法繞過這些不足來實現需求。如果可以的話,你有沒有考慮或者實踐過重新實現OC的一些功能,如果有,具體會如何做?

> 答:最大的優點是它的執行時特性,不足是沒有名稱空間,對於命名衝突,可以使用長命名法或特殊字首解決,如果是引入的第三方庫之間的命名衝突,可以使用link命令及flag解決衝突。
複製程式碼
  1. 你實現過一個框架或者庫以供別人使用麼?如果有,請談一談構建框架或者庫時候的經驗;如果沒有,請設想和設計框架的public的API,並指出大概需要如何做、需要注意一些什麼方面,來使別人容易地使用你的框架。
> 答:抽象和封裝,方便使用。首先是對問題有充分的瞭解,比如構建一個檔案解壓壓縮框架,從使用者的角度出發,只需關注傳送給框架一個解壓請求,框架完成複雜檔案的解壓操作,並且在適當的時候通知給是哦難過者,如解壓完成、解壓出錯等。在框架內部去構建物件的關係,通過抽象讓其更為健壯、便於更改。其次是API的說明文件。
複製程式碼

二、 第三方框架

  1. AFNetworking 底層原理分析

    AFNetworking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類: 1). AFHTTPRequestOperationManager:內部封裝的是 NSURLConnection, 負責傳送網路請求, 使用最多的一個類。(3.0廢棄) 2). AFHTTPSessionManager:內部封裝是 NSURLSession, 負責傳送網路請求,使用最多的一個類。 3). AFNetworkReachabilityManager:實時監測網路狀態的工具類。當前的網路環境發生改變之後,這個工具類就可以檢測到。 4). AFSecurityPolicy:網路安全的工具類, 主要是針對 HTTPS 服務。   5). AFURLRequestSerialization:序列化工具類,基類。上傳的資料轉換成JSON格式 (AFJSONRequestSerializer).使用不多。 6). AFURLResponseSerialization:反序列化工具類;基類.使用比較多: 7). AFJSONResponseSerializer; JSON解析器,預設的解析器. 8). AFHTTPResponseSerializer; 萬能解析器; JSON和XML之外的資料型別,直接返回二進 制資料.對伺服器返回的資料不做任何處理. 9). AFXMLParserResponseSerializer; XML解析器;

  2. 描述下SDWebImage裡面給UIImageView載入圖片的邏輯

    SDWebImage 中為 UIImageView 提供了一個分類UIImageView+WebCache.h, 這個分類中有一個最常用的介面sd_setImageWithURL:placeholderImage:,會在真實圖片出現前會先顯示佔點陣圖片,當真實圖片被載入出來後再替換佔點陣圖片。   載入圖片的過程大致如下: 1.首先會在 SDWebImageCache 中尋找圖片是否有對應的快取, 它會以url 作為資料的索引先在記憶體中尋找是否有對應的快取 2.如果快取未找到就會利用通過MD5處理過的key來繼續在磁碟中查詢對應的資料, 如果找到了, 就會把磁碟中的資料載入到記憶體中,並將圖片顯示出來 3.如果在記憶體和磁碟快取中都沒有找到,就會向遠端伺服器傳送請求,開始下載圖片 4.下載後的圖片會加入快取中,並寫入磁碟中 5.整個獲取圖片的過程都是在子執行緒中執行,獲取到圖片後回到主執行緒將圖片顯示出來   SDWebImage原理: 呼叫類別的方法: 1. 從記憶體(字典)中找圖片(當這個圖片在本次使用程式的過程中已經被載入過),找到直接使用。 2. 從沙盒中找(當這個圖片在之前使用程式的過程中被載入過),找到使用,快取到記憶體中。 3. 從網路上獲取,使用,快取到記憶體,快取到沙盒。

參考:SDWebImage內部實現過程

  1. 友盟統計介面統計的所有功能

    APP啟動速度,APP停留頁面時間等

三、演算法

1.不用中間變數,用兩種方法交換A和B的值

// 1.中間變數 void swap(int a, int b) { int temp = a;       a = b;       b = temp;    } // 2.加法 void swap(int a, int b) {       a = a + b;       b = a - b;       a = a - b;    } // 3.異或(相同為0,不同為1\. 可以理解為不進位加法) void swap(int a, int b) {       a = a ^ b;       b = a ^ b;       a = a ^ b;    }
複製程式碼

2.求最大公約數

/** 1.直接遍歷法 */ int maxCommonDivisor(int a, int b) { int max = 0; for (int i = 1; i <=b; i++) { if (a % i == 0 && b % i == 0) {                max = I;            }        } return max;    } /** 2.輾轉相除法 */ int maxCommonDivisor(int a, int b) { int r; while(a % b > 0) {            r = a % b;            a = b;            b = r;        } return b;    } // 擴充套件:最小公倍數 = (a * b)/最大公約數
複製程式碼

3.模擬棧操作

/**     *  棧是一種資料結構,特點:先進後出     *  練習:使用全域性變數模擬棧的操作     */ #include  #include  #include  //保護全域性變數:在全域性變數前加static後,這個全域性變數就只能在本檔案中使用 static int data[1024];//棧最多能儲存1024個資料 static int count = 0;//目前已經放了多少個數(相當於棧頂位置) //資料入棧 push void push(int x){        assert(!full());//防止陣列越界 data[count++] = x;    } //資料出棧 pop int pop(){        assert(!empty()); return data[--count];    } //檢視棧頂元素 top int top(){        assert(!empty()); return data[count-1];    } //查詢棧滿 full bool full() { if(count >= 1024) { return 1;        } return 0;     } //查詢棧空 empty bool empty() { if(count <= 0) { return 1;        } return 0;    } int main(){ //入棧 for (int i = 1; i <= 10; i++) {            push(i);        } //出棧 while(!empty()){ printf("%d ", top()); //棧頂元素 pop(); //出棧 } printf("\n"); return 0;    }
複製程式碼

4.排序演算法

選擇排序、氣泡排序、插入排序三種排序演算法可以總結為如下:

    *   都將陣列分為已排序部分和未排序部分。 1\. 選擇排序將已排序部分定義在左端,然後選擇未排序部分的最小元素和未排序部分的第一個元素交換。 2\. 氣泡排序將已排序部分定義在右端,在遍歷未排序部分的過程執行交換,將最大元素交換到最右端。 3\. 插入排序將已排序部分定義在左端,將未排序部分元的第一個元素插入到已排序部分合適的位置。 
複製程式碼

*   選擇排序 /**       *  【選擇排序】:最值出現在起始端      *        *  第1趟:在n個數中找到最小(大)數與第一個數交換位置      *  第2趟:在剩下n-1個數中找到最小(大)數與第二個數交換位置      *  重複這樣的操作...依次與第三個、第四個...數交換位置      *  第n-1趟,最終可實現資料的升序(降序)排列。      *      / void selectSort(int arr, int length) { for (int i = 0; i < length - 1; i++) { //趟數 for (int j = i + 1; j < length; j++) { //比較次數 if (arr[i] > arr[j]) {                     int temp = arr[i];                     arr[i] = arr[j];                     arr[j] = temp;                 }             }         }     }      *   氣泡排序 /       *  【氣泡排序】:相鄰元素兩兩比較,比較完一趟,最值出現在末尾      *  第1趟:依次比較相鄰的兩個數,不斷交換(小數放前,大數放後)逐個推進,最值最後出現在第n個元素位置      *  第2趟:依次比較相鄰的兩個數,不斷交換(小數放前,大數放後)逐個推進,最值最後出現在第n-1個元素位置      *   ……   ……      *  第n-1趟:依次比較相鄰的兩個數,不斷交換(小數放前,大數放後)逐個推進,最值最後出現在第2個元素位置       */ void bublleSort(int *arr, int length) { for(int i = 0; i < length - 1; i++) { //趟數 for(int j = 0; j < length - i - 1; j++) { //比較次數 if(arr[j] > arr[j+1]) {                     int temp = arr[j];                     arr[j] = arr[j+1];                     arr[j+1] = temp;                 }             }          }     }


參考: 8大排序演算法圖文講解 常用演算法OC實現


5.折半查詢(二分查詢)

/**     *  折半查詢:優化查詢時間(不用遍歷全部資料)     *     *  折半查詢的原理:     *   1> 陣列必須是有序的     *   2> 必須已知min和max(知道範圍)     *   3> 動態計算mid的值,取出mid對應的值進行比較     *   4> 如果mid對應的值大於要查詢的值,那麼max要變小為mid-1     *   5> 如果mid對應的值小於要查詢的值,那麼min要變大為mid+1     *     */ // 已知一個有序陣列, 和一個key, 要求從陣列中找到key對應的索引位置  int findKey(int *arr, int length, int key) { int min = 0, max = length - 1, mid; while (min <= max) {            mid = (min + max) / 2; //計算中間值 if (key > arr[mid]) {                min = mid + 1;            } else if (key < arr[mid]) {                max = mid - 1;            } else { return mid;            }        } return -1;    }
複製程式碼

四、編碼格式(優化細節)

參考:請Review下面的程式碼,並根據iOS的編碼規範做出正確的修改

1.在 Objective-C 中,enum 建議使用NS_ENUM和NS_OPTIONS巨集來定義列舉型別。

//定義一個列舉(比較嚴密) typedef NS_ENUM(NSInteger, BRUserGender) {
        BRUserGenderUnknown, // 未知 BRUserGenderMale, // 男性 BRUserGenderFemale, // 女性 BRUserGenderNeuter // 無性 }; @interface BRUser : NSObject @property (nonatomic, readonly, copy) NSString *name; @property (nonatomic, readonly, assign) NSUInteger age; @property (nonatomic, readonly, assign) BRUserGender gender;

    - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender; @end //說明: //既然該類中已經有一個“初始化方法” ,用於設定 name、age 和 gender 的初始值: 那麼在設計對應 @property 時就應該儘量使用不可變的物件:其三個屬性都應該設為“只讀”。用初始化方法設定好屬性值之後,就不能再改變了。 //屬性的引數應該按照下面的順序排列: (原子性,讀寫,記憶體管理)
複製程式碼

2.避免使用C語言中的基本資料型別,建議使用 Foundation 資料型別,對應關係如下:

int -> NSInteger unsigned -> NSUInteger float -> CGFloat 動畫時間 -> NSTimeInterval

五、其它知識點

  1. HomeKit,是蘋果2014年釋出的智慧家居平臺。

  2. 什麼是 OpenGL、Quartz 2D?

    Quatarz 2d 是Apple提供的基本圖形工具庫。只是適用於2D圖形的繪製。 OpenGL,是一個跨平臺的圖形開發庫。適用於2D和3D圖形的繪製。

  3. ffmpeg框架:

    ffmpeg 是音視訊處理工具,既有音視訊編碼解碼功能,又可以作為播放器使用。

  4. 談談 UITableView 的優化

    1). 正確的複用cell; 2). 設計統一規格的Cell; 3). 提前計算並快取好高度(佈局),因為heightForRowAtIndexPath:是呼叫最頻繁的方法; 4). 非同步繪製,遇到複雜介面,遇到效能瓶頸時,可能就是突破口; 5). 滑動時按需載入,這個在大量圖片展示,網路載入的時候很管用! 6). 減少子檢視的層級關係; 7). 儘量使所有的檢視不透明化以及做切圓操作; 8). 不要動態的add 或者 remove 子控制元件。最好在初始化時就新增完,然後通過hidden來控制是否顯示; 9). 使用除錯工具分析問題。

  5. 如何實行cell的動態的行高

    如果希望每條資料顯示自身的行高,必須設定兩個屬性,1.預估行高,2.自定義行高。 設定預估行高 tableView.estimatedRowHeight = 200。 設定定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。 如果要讓自定義行高有效,必須讓容器檢視有一個自下而上的約束。

  6. 說說你對 block 的理解

    棧上的自動複製到堆上,block 的屬性修飾符是 copy,迴圈引用的原理和解決方案。   block的迴圈引用;block的程式碼實現;為什麼會造成迴圈引用;block是如何強引用self的; 參考:Block 程式碼塊中用到了self引用問題,導致迴圈引用

  7. 什麼是野指標、空指標?

    野指標:不知道指向了哪裡的指標叫野指標。即指標指向不確定,指標存的地址是一個垃圾值,未初始化。 空指標:不指向任何位置的指標叫空指標。即指標沒有指向,指標存的地址是一個空地址,NULL。

  8. 什麼是 OOA / OOD / OOP ?

    OOA(Object Oriented Analysis) --物件導向分析 OOD(Object Oriented Design) --物件導向設計 OOP(Object Oriented Programming)--物件導向程式設計

  9. 多執行緒是什麼

    多執行緒是個複雜的概念,按字面意思是同步完成多項任務,提高了資源的使用效率,從硬體、作業系統、應用軟體不同的角度去看,多執行緒被賦予不同的內涵,對於硬體,現在市面上多數的CPU都是多核的,多核的CPU運算多執行緒更為出色;從作業系統角度,是多工,現在用的主流作業系統都是多工的,可以一邊聽歌、一邊寫部落格;對於應用來說,多執行緒可以讓應用有更快的回應,可以在網路下載時,同時響應使用者的觸控操作。在iOS應用中,對多執行緒最初的理解,就是併發,它的含義是原來先做燒水,再摘菜,再炒菜的工作,會變成燒水的同時去摘菜,最後去炒菜。

  10. iOS 中的多執行緒

> iOS中的多執行緒,是Cocoa框架下的多執行緒,通過Cocoa的封裝,可以讓我們更為方便的使用執行緒,做過C++的同學可能會對執行緒有更多的理解,比如執行緒的創立,訊號量、共享變數有認識,Cocoa框架下會方便很多,它對執行緒做了封裝,有些封裝,可以讓我們建立的物件,本身便擁有執行緒,也就是執行緒的物件化抽象,從而減少我們的工程,提供程式的健壯性。
>  
> GCD是(Grand Central Dispatch)的縮寫 ,從系統級別提供的一個易用地多執行緒類庫,具有執行時的特點,能充分利用多核心硬體。GCD的API介面為C語言的函式,函式引數中多數有Block,關於Block的使用參看這裡,為我們提供強大的“介面”,對於GCD的使用參見本文
>  
> NSOperation與Queue
>  
> NSOperation是一個抽象類,它封裝了執行緒的細節實現,我們可以通過子類化該物件,加上NSQueue來同物件導向的思維,管理多執行緒程式。具體可參看這裡:一個基於NSOperation的多執行緒網路訪問的專案。
>  
> NSThread
>  
> NSThread是一個控制執行緒執行的物件,它不如NSOperation抽象,通過它我們可以方便的得到一個執行緒,並控制它。但NSThread的執行緒之間的併發控制,是需要我們自己來控制的,可以通過NSCondition實現。
>  
> 參看 iOS多執行緒程式設計之NSThread的使用 
>  
> 其他多執行緒
>  
> 在Cocoa的框架下,通知、Timer和非同步函式等都有使用多執行緒,(待補充).
複製程式碼
  1. 在專案什麼時候選擇使用GCD,什麼時候選擇NSOperation?
> 專案中使用NSOperation的優點是NSOperation是對執行緒的高度抽象,在專案中使用它,會使專案的程式結構更好,子類化NSOperation的設計思路,是具有物件導向的優點(複用、封裝),使得實現是多執行緒支援,而介面簡單,建議在複雜專案中使用。
>  
> 專案中使用GCD的優點是GCD本身非常簡單、易用,對於不復雜的多執行緒操作,會節省程式碼量,而Block引數的使用,會是程式碼更為易讀,建議在簡單專案中使用。
複製程式碼
  1. KVO,NSNotification,delegate及block區別
> * KVO就是cocoa框架實現的觀察者模式,一般同KVC搭配使用,通過KVO可以監測一個值的變化,比如View的高度變化。是一對多的關係,一個值的變化會通知所有的觀察者。
> * NSNotification是通知,也是一對多的使用場景。在某些情況下,KVO和NSNotification是一樣的,都是狀態變化之後告知對方。NSNotification的特點,就是需要被觀察者先主動發出通知,然後觀察者註冊監聽後再來進行響應,比KVO多了傳送通知的一步,但是其優點是監聽不侷限於屬性的變化,還可以對多種多樣的狀態變化進行監聽,監聽範圍廣,使用也更靈活。
>  
> * delegate 是代理,就是我不想做的事情交給別人做。比如狗需要吃飯,就通過delegate通知主人,主人就會給他做飯、盛飯、倒水,這些操作,這些狗都不需要關心,只需要呼叫delegate(代理人)就可以了,由其他類完成所需要的操作。所以delegate是一對一關係。
>  
> * block是delegate的另一種形式,是函數語言程式設計的一種形式。使用場景跟delegate一樣,相比delegate更靈活,而且代理的實現更直觀。
>  
> * KVO一般的使用場景是資料,需求是資料變化,比如股票價格變化,我們一般使用KVO(觀察者模式)。delegate一般的使用場景是行為,需求是需要別人幫我做一件事情,比如買賣股票,我們一般使用delegate。
> Notification一般是進行全域性通知,比如利好訊息一出,通知大家去買入。delegate是強關聯,就是委託和代理雙方互相知道,你委託別人買股票你就需要知道經紀人,經紀人也不要知道自己的顧客。Notification是弱關聯,利好訊息發出,你不需要知道是誰發的也可以做出相應的反應,同理發訊息的人也不需要知道接收的人也可以正常發出訊息。
複製程式碼
  1. 將一個函式在主執行緒執行的4種方法
*   GCD方法,通過向主執行緒佇列傳送一個block塊,使block裡的方法可以在主執行緒中執行。 dispatch_async(dispatch_get_main_queue(), ^{ //需要執行的方法 });

    * NSOperation 方法 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主佇列 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ //需要執行的方法 }];
    [mainQueue addOperation:operation];

    * NSThread 方法

    [self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];

    [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];

    [[NSThread mainThread] performSelector:@selector(method) withObject:nil];

    *   RunLoop方法

    [[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];
複製程式碼
  1. 如何讓計時器呼叫一個類方法

    • 計時器只能呼叫例項方法,但是可以在這個例項方法裡面呼叫靜態方法。
    • 使用計時器需要注意,計時器一定要加入RunLoop中,並且選好model才能執行。scheduledTimerWithTimeInterval方法建立一個計時器並加入到RunLoop中所以可以直接使用。
    • 如果計時器的repeats選擇YES說明這個計時器會重複執行,一定要在合適的時機呼叫計時器的invalid。不能在dealloc中呼叫,因為一旦設定為repeats 為yes,計時器會強持有self,導致dealloc永遠不會被呼叫,這個類就永遠無法被釋放。比如可以在viewDidDisappear中呼叫,這樣當類需要被回收的時候就可以正常進入dealloc中了。
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    
    -(void)timerMethod
    { //呼叫類方法 [[self class] staticMethod];
    }
    
    -(void)invalid
    {
        [timer invalid];
        timer = nil;
    }
    複製程式碼
  2. 如何重寫類方法

    • 1、在子類中實現一個同基類名字一樣的靜態方法

    • 2、在呼叫的時候不要使用類名呼叫,而是使用[self class]的方式呼叫。原理,用類名呼叫是早繫結,在編譯期繫結,用[self class]是晚繫結,在執行時決定呼叫哪個方法。

  3. NSTimer建立後,會在哪個執行緒執行

    • 用scheduledTimerWithTimeInterval建立的,在哪個執行緒建立就會被加入哪個執行緒的RunLoop中就執行在哪個執行緒

    • 自己建立的Timer,加入到哪個執行緒的RunLoop中就執行在哪個執行緒。

  4. id和NSObject*的區別

    • id是一個 objc_object 結構體指標,定義是
    typedef struct objc_object *id
    複製程式碼
    • id可以理解為指向物件的指標。所有oc的物件 id都可以指向,編譯器不會做型別檢查,id呼叫任何存在的方法都不會在編譯階段報錯,當然如果這個id指向的物件沒有這個方法,該崩潰還是會崩潰的。

    • NSObject *指向的必須是NSObject的子類,呼叫的也只能是NSObjec裡面的方法否則就要做強制型別轉換。

    • 不是所有的OC物件都是NSObject的子類,還有一些繼承自NSProxy。NSObject *可指向的型別是id的子集。

  5. static關鍵字的作用

  • 回答一:

    1.在函式體內定義的static他的作用域為該函式體,該變數在記憶體中只被分配一次,因此,其值在下次呼叫時仍維持上次的值不變; 2.在模組內的static全域性變數可以被模組內所有函式訪問,但是不能被模組外的其他函式訪問; 3.在模組內的staic全域性變數可以被這一模組內的其他函式呼叫,這個函式的使用範圍被限制在這個模組內; 4.在類中的static成員變數屬於整個類所擁有,對類的所有物件只有一份拷貝,也就是說只要是該類的物件,那麼該物件的中被static修飾的成員變數都指向同一塊地址。

  • 回答二:

    修飾區域性變數: 1.延長區域性變數的生命週期,程式結束才會銷燬。 2.區域性變數只會生成一份記憶體,只會初始化一次。 3.改變區域性變數的作用域。   修飾全域性變數: 1.只能在本檔案中訪問,修改全域性變數的作用域,生命週期不會改 2.避免重複定義全域性變數

  • 在OC中static關鍵字使用誤區

    1.使用static修飾例項變數是不被允許的 2.使用static修飾了方法,也是錯誤的

  1. 使用 Swift 語言程式設計的優缺點 總的來說,我認為使用 Swift 來作為程式語言的優點還是要遠遠大於缺點的,而且很多缺點蘋果也在逐漸改善。
  • 優點:

    1、簡潔的語法 2、更強的型別安全 3、函數語言程式設計的支援    Swift 語言本身提供了對函數語言程式設計的支援。    Objc 本身是不支援的,但是可以通過引入 ReactiveCocoa 這個庫來支援函數語言程式設計。 4、編寫 OS X 下的自動化指令碼

  • 缺點

    1、App體積變大     使用Swift 後, App 體積大概增加 5-8 M 左右,對體積大小敏感的慎用。     體積變大的原因是因為 Swift 還在變化,所以 Apple 沒有在 iOS 系統裡放入 Swift 的執行庫,反而是每個 App 裡都要包含其對應的 Swift 執行庫。 2、Xcode 支援不夠好     如果你是使用 Xcode經常卡住或者崩潰想必你是肯定碰到過了,這個是目前使用 Swift 最讓人頭疼的事情,即使是到現在XCode 9, 有時候也會遇到這種問題,所以要看你的承受力了…… 3、第三方庫的支援不夠多     目前確實 Swift 編寫的第三方庫確實不多,但可以通過橋接的方式來使用 Objc 的三方庫,基本上沒有太大問題。現在已經改善很多了… 4、語言版本更新帶來的編譯問題    語言本身還在發展,所以每次版本更新後都會出現編譯不過的情況(至少到目前為止還是),但是自從 4.0 版本釋出後,改動沒有 beta 時候那麼大了,而且根據 Xcode 提示基本就可以解決語法變動導致的編譯問題了。

#小編這裡推薦一個群:691040931 裡面有大量的書籍和麵試資料,很多的iOS開發者都在裡面交流技術

面試資料截圖.jpg
本文轉載於 blog.csdn.net/wujakf/arti…

相關文章