2020年,精選大廠的iOS面試題總結(jié)(一)

iOS面試題目錄(一)

  • 1. iOS內(nèi)存管理機(jī)制
  • 2. NSThread、GCD、NSOperation多線程
  • 3. 輸入一個字符串,判斷這個字符串是否是有效的IP地址
  • 4. 大數(shù)加法怎么實(shí)現(xiàn)?
  • 5. 簡述KVC和KVO,其中KVO實(shí)現(xiàn)原理?
  • 6. Block實(shí)現(xiàn)原理;堆上和棧上的數(shù)據(jù)如何同步?
  • 7. iOS設(shè)計(jì)模式
  • 8. 多線程有哪些?如何保證多線程中讀寫分離,加鎖方案?
  • 9. 如何刪除單鏈表中一個元素?
  • 10. NSNotificationCenter通知中心的實(shí)現(xiàn)原理?
  • 11. 推送如何實(shí)現(xiàn)的?
  • 12. SEL的使用和原理?
  • 13. 點(diǎn)擊事件如何穿透透明的View?
  • 14. RunLoop的實(shí)現(xiàn)原理?(答案待完善)
  • 15. 簡述Runtime,發(fā)送消息的過程;
  • 16. 簡述weak的實(shí)現(xiàn)原理;
  • 17. 寫一個單例;
  • 18. 如何從字符串中得到一個整數(shù)?
  • 19. 數(shù)組去重方式;
  • 20. 設(shè)計(jì)一個數(shù)據(jù)庫;(答案待完善)
  • 21. 實(shí)現(xiàn)多個網(wǎng)絡(luò)請求ABC執(zhí)行完再執(zhí)行D
  • 22. 列表頁性能優(yōu)化
  • 23. HTTPS(答案待完善)
  • 23. 音視頻相關(guān)

1. iOS內(nèi)存管理機(jī)制

iOS內(nèi)存管理機(jī)制的原理是引用計(jì)數(shù),當(dāng)這塊內(nèi)存被創(chuàng)建后,它的引用計(jì)數(shù)0->1,表示有一個對象或指針持有這塊內(nèi)存,擁有這塊內(nèi)存的所有權(quán),如果這時(shí)候有另外一個對象或指針指向這塊內(nèi)存,那么為了表示這個后來的對象或指針對這塊內(nèi)存的所有權(quán),引用計(jì)數(shù)1->2,之后若有一個對象或指針不再指向這塊內(nèi)存時(shí),引用計(jì)數(shù)-1,表示這個對象或指針不再擁有這塊內(nèi)存的所有權(quán),當(dāng)一塊內(nèi)存的引用計(jì)數(shù)變?yōu)?,表示沒有任何對象或指針持有這塊內(nèi)存,系統(tǒng)便會立刻釋放掉這塊內(nèi)存。

  • alloc、new :類初始化方法,開辟新的內(nèi)存空間,引用計(jì)數(shù)+1;
  • retain :實(shí)例方法,不會開辟新的內(nèi)存空間,引用計(jì)數(shù)+1;
  • copy : 實(shí)例方法,把一個對象復(fù)制到新的內(nèi)存空間,新的內(nèi)存空間引用計(jì)數(shù)+1,舊的不會;其中分為淺拷貝和深拷貝,淺拷貝只是拷貝地址,不會開辟新的內(nèi)存空間;深拷貝是拷貝內(nèi)容,會開辟新的內(nèi)存空間;
  • strong :強(qiáng)引用; 引用計(jì)數(shù)+1;
  • release :實(shí)例方法,釋放對象;引用計(jì)數(shù)-1;
  • autorelease : 延遲釋放;autoreleasepool自動釋放池;當(dāng)執(zhí)行完之后引用計(jì)數(shù)-1;
  • 還有是initWithFormat和stringWithFormat 字符串長度大于9時(shí),引用計(jì)數(shù)+1;
  • assign : 弱引用 ;weak也是弱引用,兩者區(qū)別:assign不但能作用于對象還能作用于基本數(shù)據(jù)類型,但是所指向的對象銷毀時(shí)不會將當(dāng)前指向?qū)ο蟮闹羔樦赶騨il,有野指針的生成;weak只能作用于對象,不能作用于基本數(shù)據(jù)類型,所指向的對象銷毀時(shí)會將當(dāng)前指向?qū)ο蟮闹羔樦赶騨il,防止野指針的生成。

2. NSThread、GCD、NSOperation多線程

  • 1、NSThread

NSThread是封裝程度最小最輕量級的,使用更靈活,但要手動管理線程的生命周期、線程同步和線程加鎖等,開銷較大;

[NSThread isMultiThreaded];//BOOL 是否開啟了多線程  
[NSThread currentThread];//NSThread 獲取當(dāng)前線程  
[NSThread mainThread];//NSThread 獲取主線程  
[NSThread sleepForTimeInterval:1];//線程睡眠1s

  • 2、GCD

GCD基于C語言封裝的,遵循FIFO

dispatch_sync與dispatch_async//同步和異步操作

dispatch_queue_t;//主要有串行和并發(fā)兩種;
    其中:
    dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT)并發(fā);
    dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL)串行;

dispatch_once_t;//代碼只會被執(zhí)行一次,用于單例
dispatch_after;//延遲操作
dispatch_get_main_queue;//回到主線程操作

//Demo單例
+ (instancetype)sharedInstance {
    static ZZScreenshotsMonitor *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

//Demo:執(zhí)行順序
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"1");
    });

    NSLog(@"2");

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);

    dispatch_sync(queue, ^{
        NSLog(@"3");
    });

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"4");
    });

    dispatch_async(queue, ^{
        NSLog(@"5");
    });

    NSLog(@"6");

    [self performSelector:@selector(delayMethod) withObject:nil afterDelay:0];

    NSLog(@"8");
}

- (void)delayMethod {
    NSLog(@"7");
}

打印結(jié)果:23658147;其中5和8隨機(jī)調(diào)換

  • NSOperation

NSOperation基于GCD封裝的,比GCD可控性更強(qiáng);可以加入操作依賴(addDependency)、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的操作個數(shù)(setMaxConcurrentOperationCount)、取消操作(cancel)等,需要使用兩個它的實(shí)體子類:NSBlockOperation和NSInvocationOperation,或者繼承NSOperation自定義子類;NSBlockOperation和NSInvocationOperation用法的主要區(qū)別是:前者執(zhí)行指定的方法,后者執(zhí)行代碼塊,相對來說后者更加靈活易用。NSOperation操作配置完成后便可調(diào)用start函數(shù)在當(dāng)前線程執(zhí)行,如果要異步執(zhí)行避免阻塞當(dāng)前線程則可以加入NSOperationQueue中異步執(zhí)行

作為一個開發(fā)者,有一個學(xué)習(xí)的氛圍跟一個交流圈子特別重要,這是小編的一個交流群,點(diǎn)擊加入群聊 iOS開發(fā)交流 :937194184;不管你是小白還是大牛歡迎入駐 ,分享BAT等各大廠面試題、面試經(jīng)驗(yàn),討論技術(shù),大家一起交流學(xué)習(xí)成長!

3.輸入一個字符串,判斷這個字符串是否是有效的IP地址

+ (BOOL)isValidIP:(NSString *)ipStr {
    if (nil == ipStr) {
        return NO;
    }

    NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
    if (ipArray.count == 4) {
        for (NSString *ipnumberStr in ipArray) {
              if ([self isPureInt:ipnumberStr]) {
                  int ipnumber = [ipnumberStr intValue];
                if (!(ipnumber>=0 && ipnumber<=255)) {
                    return NO;
                }
              }            
        }
        return YES;
    }
    return NO;
}
//是否整形
- (BOOL)isPureInt:(NSString*)string {
    NSScanner* scan = [NSScanner scannerWithString:string];
    int val;
    return[scan scanInt:&val] && [scan isAtEnd];
}
//是否只含有數(shù)字
- (BOOL)validateNumber:(NSString*)number {
    BOOL res = YES;
    NSCharacterSet* tmpSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
    int i = 0;
    while (i < number.length) {
        NSString * string = [number substringWithRange:NSMakeRange(i, 1)];
        NSRange range = [string rangeOfCharacterFromSet:tmpSet];
        if (range.length == 0) {
            res = NO;
            break;
        }
        i++;
    }
    return res;

}

4.大數(shù)加法怎么實(shí)現(xiàn)?

使用字符串實(shí)現(xiàn);

/兩個大數(shù)相加算法
-(NSString *)addTwoNumberWithOneNumStr:(NSString *)one anotherNumStr:(NSString *)another
{
    int i = 0;
    int j = 0;
    int maxLength = 0;
    int sum = 0;
    int overflow = 0;
    int carryBit = 0;
    NSString *temp1 = @"";
    NSString *temp2 = @"";
    NSString *sums = @"";
    NSString *tempSum = @"";
    int length1 = (int)one.length;
    int length2 = (int)another.length;
    //1.反轉(zhuǎn)字符串
    for (i = length1 - 1; i >= 0 ; i--) {
        NSRange range = NSMakeRange(i, 1);
        temp1 = [temp1 stringByAppendingString:[one substringWithRange:range]];
        NSLog(@"%@",temp1);
    }
    for (j = length2 - 1; j >= 0; j--) {
        NSRange range = NSMakeRange(j, 1);
        temp2 = [temp2 stringByAppendingString:[another substringWithRange:range]];
        NSLog(@"%@",temp2);
    }

    //2.補(bǔ)全缺少位數(shù)為0
    maxLength = length1 > length2 ? length1 : length2;
    if (maxLength == length1) {
        for (i = length2; i < length1; i++) {
            temp2 = [temp2 stringByAppendingString:@"0"];
            NSLog(@"i = %d --%@",i,temp2);
        }
    }else{
        for (j = length1; j < length2; j++) {
            temp1 = [temp1 stringByAppendingString:@"0"];
            NSLog(@"j = %d --%@",j,temp1);
        }
    }
    //3.取數(shù)做加法
    for (i = 0; i < maxLength; i++) {
        NSRange range = NSMakeRange(i, 1);
        int a = [temp1 substringWithRange:range].intValue;
        int b = [temp2 substringWithRange:range].intValue;
        sum = a + b + carryBit;
        if (sum > 9) {
            if (i == maxLength -1) {
                overflow = 1;
            }
            carryBit = 1;
            sum -= 10;
        }else{
            carryBit = 0;
        }
        tempSum = [tempSum stringByAppendingString:[NSString stringWithFormat:@"%d",sum]];
    }
    if (overflow == 1) {
        tempSum = [tempSum stringByAppendingString:@"1"];
    }
    int sumlength = (int)tempSum.length;
    for (i = sumlength - 1; i >= 0 ; i--) {
        NSRange range = NSMakeRange(i, 1);
        sums = [sums stringByAppendingString:[tempSum substringWithRange:range]];
    }
    NSLog(@"sums = %@",sums);
    return sums;
}

5.簡述KVC和KVO,其中KVO實(shí)現(xiàn)原理?

KVC : 鍵值編碼(Key-Value Coding),它是一種通過key值訪問類屬性的機(jī)制,而不是通過setter/getter方法訪問。其中 KVC 原理:當(dāng)調(diào)用- (void)setValue:(id)value forUndefinedKey:(NSString *)key時(shí),KVC底層的執(zhí)行機(jī)制如下:
首先搜索對應(yīng)屬性的setter方法
如果沒有找到屬性的setter方法,則會檢查+ (BOOL)accessInstanceVariablesDirectly方法是否返回了YES(該方法默認(rèn)返回YES),如果返回了YES, 則KVC機(jī)制會搜索類中是否存在該屬性的成員變量,也就是_屬性名,存在則對該成員變量賦值。搜索成員變量名的順序是 _key,_isKey,key,isKey。
另外我們也可以通過重寫+ (BOOL)accessInstanceVariablesDirectly方法返回NO,這個時(shí)候KVC機(jī)制就會調(diào)用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。
如果沒有找到成員變量,調(diào)用 - (void)setValue:(id)value forUndefinedKey:(NSString *)key。

KVO : 鍵值觀察者 (Key-Value Observer): KVO 是觀察者模式的一種實(shí)現(xiàn),觀察者A監(jiān)聽被觀察者B的某個屬性,當(dāng)B的屬性發(fā)生更改時(shí),A就會收到通知,執(zhí)行相應(yīng)的方法。實(shí)現(xiàn)原理:基本的原理:當(dāng)觀察某對象A時(shí),KVO機(jī)制動態(tài)創(chuàng)建一個對象A當(dāng)前類的子類,并為這個新的子類重寫了被觀察屬性keyPath的setter 方法。setter 方法隨后負(fù)責(zé)通知觀察對象屬性的改變狀況。

Apple 使用了 isa 混寫(isa-swizzling)來實(shí)現(xiàn) KVO 。當(dāng)觀察對象A時(shí),KVO機(jī)制動態(tài)創(chuàng)建一個新的名為:NSKVONotifying_A 的新類,該類繼承自對象A的本類,且 KVO 為 NSKVONotifying_A 重寫觀察屬性的 setter 方法,setter 方法會負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察對象屬性值的更改情況。

子類setter方法剖析:KVO 的鍵值觀察通知依賴于 NSObject 的兩個方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數(shù)值的前后分別調(diào)用 2 個方法:
被觀察屬性發(fā)生改變之前,willChangeValueForKey:被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值即將變更;當(dāng)改變發(fā)生后, didChangeValueForKey: 被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值已經(jīng)變更;之后, observeValueForKey:ofObject:change:context: 也會被調(diào)用。且重寫觀察屬性的 setter 方法這種繼承方式的注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。

如果賦值沒有通過 setter 方法或者 KVC,而是直接修改屬性對應(yīng)的成員變量,例如:僅調(diào)用 _name = @“newName”,這時(shí)是不會觸發(fā) KVO 機(jī)制,更加不會調(diào)用回調(diào)方法的

6.Block實(shí)現(xiàn)原理;堆上和棧上的數(shù)據(jù)如何同步?

block本質(zhì)上也是一個oc對象,他內(nèi)部也有一個isa指針。block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象。結(jié)構(gòu)體,在棧上的情況, Block中的指針只是指向棧上的__block變量, 而當(dāng)Block/__block變量被copy到堆上以后, 堆上Block會持有堆上__block變量. 而堆上的Block再次被調(diào)用copy時(shí), 只是Block的引用計(jì)數(shù)+1而已, 而__block變量如果被多個堆上Block持有也只涉及到引用記數(shù)的變化. 一旦Block/__block變量的引用計(jì)數(shù)為0, 就會自動從堆上釋放內(nèi)存.這里Block/__block變量在堆上的內(nèi)存管理與Objective-C對象完全一致.

Block類                  原存儲域    調(diào)用copy效果
_NSConcreteStackBlock   棧           從棧copy到堆
_NSConcreteGlobalBlock  數(shù)據(jù)域(.data域) 什么也不做
_NSConcreteMallocBlock  堆           引用計(jì)數(shù)+1

7.iOS設(shè)計(jì)模式

  • 構(gòu)造模式

構(gòu)造模式用于將某個業(yè)務(wù)的屬性和行為進(jìn)行分離,當(dāng)業(yè)務(wù)屬性越多的時(shí)候該模式用起來就越方便。比如:我要自定義一個比較靈活的彈窗,這個彈窗有顯示和隱藏、動畫的功能,并且彈窗的大小、樣式顯示的位置都要可以自定義。這樣我們就可以使用構(gòu)造模式,將行為和屬性分離出來,彈窗的顯示和隱藏就是行為,其他的均為屬性,這些屬性的構(gòu)造過程中就可以被定義好.

  • 適配器模式;

1:何為適配器模式?
適配器模式將一個類的接口適配成用戶所期待的。一個適配器通常允許因?yàn)榻涌诓患嫒荻荒芤黄鸸ぷ鞯念惸軌蛟谝黄鸸ぷ鳎龇ㄊ菍㈩愖约旱慕涌诎谝粋€已存在的類中。
2:[如何使用適配器模式?]
當(dāng)你想使用一個已經(jīng)存在的類,而它的接口不符合你的需求;
你想創(chuàng)建一個可以復(fù)用的類,該類可以與其他不相關(guān)的類或不可預(yù)見的類協(xié)同工作;
你想使用一些已經(jīng)存在的子類,但是不可能對每一個都進(jìn)行子類化以匹配它們的接口,對象適配器可以適配它的父親接口。
3:[適配器模式的優(yōu)缺點(diǎn)?]
優(yōu)點(diǎn):降低數(shù)據(jù)層和視圖層(對象)的耦合度,使之使用更加廣泛,適應(yīng)復(fù)雜多變的變化。
缺點(diǎn):降低了可讀性,代碼量增加,對于不理解這種模式的人來說比較難看懂。

  • 策略模式;

1:何為策略模式?策略模式定義了一系列的算法,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化。
2:如何使用策略模式?
在有多種算法相似的情況下,使用 if…else 所帶來的復(fù)雜和難以維護(hù)。
如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)。
注意事項(xiàng):如果一個系統(tǒng)的策略多于四個,就需要考慮使用混合模式,解決策略類膨脹的問題。
3:策略模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):簡化操作,提高代碼維護(hù)性。算法可以自由切換,避免使用多重條件判斷,擴(kuò)展性良好。
缺點(diǎn):在使用之前就要確定使用某種策略,而不是動態(tài)的選擇策略。策略類會增多,所有策略類都需要對外暴露。

  • 觀察者模式;

1:[何為觀察者模式?]
當(dāng)對象間存在一對多關(guān)系時(shí),則使用觀察者模式(Observer Pattern)。比如,當(dāng)一個對象被修改時(shí),則會自動通知它的依賴對象。觀察者模式屬于行為型模式。
2:如何使用觀察者模式?
一個對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。一個對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進(jìn)行廣播通知。
3:觀察者模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):觀察者和被觀察者是抽象耦合的。建立一套觸發(fā)機(jī)制。缺點(diǎn):如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費(fèi)很多時(shí)間。如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。

  • 原型/外觀模式;

1:何為原型/外觀模式?
原型模式:(Prototype Pattern)用于創(chuàng)建重復(fù)的對象,同時(shí)又能保證性能。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。這種模式是實(shí)現(xiàn)了一個原型接口,該接口用于創(chuàng)建當(dāng)前對象的克隆。當(dāng)直接創(chuàng)建對象的代價(jià)比較大時(shí),則采用這種模式。
外觀模式:(Facade Pattern)隱藏系統(tǒng)的復(fù)雜性,并向客戶端提供了一個客戶端可以訪問系統(tǒng)的接口。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它向現(xiàn)有的系統(tǒng)添加一個接口,來隱藏系統(tǒng)的復(fù)雜性。這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現(xiàn)有系統(tǒng)類方法的委托調(diào)用。
2:如何使用原型/外觀模式?
原型模式:
當(dāng)一個系統(tǒng)應(yīng)該獨(dú)立于它的產(chǎn)品創(chuàng)建,構(gòu)成和表示時(shí)。
當(dāng)要實(shí)例化的類是在運(yùn)行時(shí)刻指定時(shí),例如,通過動態(tài)裝載。
為了避免創(chuàng)建一個與產(chǎn)品類層次平行的工廠類層次時(shí)。
當(dāng)一個類的實(shí)例只能有幾個不同狀態(tài)組合中的一種時(shí)。建立相應(yīng)數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實(shí)例化該類更方便一些。
外觀模式:
客戶端不需要知道系統(tǒng)內(nèi)部的復(fù)雜聯(lián)系,整個系統(tǒng)只需提供一個"接待員"即可。
定義系統(tǒng)的入口。
3:原型/外觀模式的優(yōu)缺點(diǎn)?
原型模式:
優(yōu)點(diǎn):性能提高,逃避構(gòu)造函數(shù)的約束。
缺點(diǎn):
配備克隆方法需要對類的功能進(jìn)行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易。
必須實(shí)現(xiàn) Cloneable 接口。
逃避構(gòu)造函數(shù)的約束。
外觀模式
優(yōu)點(diǎn):減少系統(tǒng)相互依賴、提高靈活性、提高了安全性。
缺點(diǎn):不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。

  • 工廠模式;

1:何為工廠模式?
這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
在工廠模式中,我們在創(chuàng)建對象時(shí)不會對客戶端暴露創(chuàng)建邏輯,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象。
2:如何使用工廠模式?
我們明確地計(jì)劃不同條件下創(chuàng)建不同實(shí)例時(shí)。
作為一種創(chuàng)建類模式,在任何需要生成復(fù)雜對象的地方,都可以使用工廠方法模式。有一點(diǎn)需要注意的地方就是復(fù)雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創(chuàng)建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統(tǒng)的復(fù)雜度。
3:工廠模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。
擴(kuò)展性高,如果想增加一個產(chǎn)品,只要擴(kuò)展一個工廠類就可以。
屏蔽產(chǎn)品的具體實(shí)現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口。
缺點(diǎn):
每次增加一個產(chǎn)品時(shí),都需要增加一個具體類和對象實(shí)現(xiàn)工廠,使得系統(tǒng)中類的個數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,同時(shí)也增加了系統(tǒng)具體類的依賴。這并不是什么好事。

  • 橋接模式;

1:何為橋接模式?
橋接(Bridge)是用于把抽象化與實(shí)現(xiàn)化解耦,使得二者可以獨(dú)立變化。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它通過提供抽象化和實(shí)現(xiàn)化之間的橋接結(jié)構(gòu),來實(shí)現(xiàn)二者的解耦。
這種模式涉及到一個作為橋接的接口,使得實(shí)體類的功能獨(dú)立于接口實(shí)現(xiàn)類。這兩種類型的類可被結(jié)構(gòu)化改變而互不影響。
2:如何使用橋接模式?
在有多種可能會變化的情況下,用繼承會造成類爆炸問題,擴(kuò)展起來不靈活。
實(shí)現(xiàn)系統(tǒng)可能有多個角度分類,每一種角度都可能變化。
把這種多角度分類分離出來,讓它們獨(dú)立變化,減少它們之間耦合。
3:橋接模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn) :抽象和實(shí)現(xiàn)的分離、優(yōu)秀的擴(kuò)展能力、實(shí)現(xiàn)細(xì)節(jié)對客戶透明。
缺點(diǎn):橋接模式的引入會增加系統(tǒng)的理解與設(shè)計(jì)難度,由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對抽象進(jìn)行設(shè)計(jì)與編程。

  • 代理模式;

1:何為代理模式?
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。
在代理模式中,我們創(chuàng)建具有現(xiàn)有對象的對象,以便向外界提供功能接口。
2:如何使用代理模式?
在直接訪問對象時(shí)帶來的問題,比如說:要訪問的對象在遠(yuǎn)程的機(jī)器上。在面向?qū)ο笙到y(tǒng)中,有些對象由于某些原因(比如對象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪問),直接訪問會給使用者或者系統(tǒng)結(jié)構(gòu)帶來很多麻煩,我們可以在訪問此對象時(shí)加上一個對此對象的訪問層。
想在訪問一個類時(shí)做一些控制。
3:代理模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
職責(zé)清晰、高擴(kuò)展性、智能化。
缺點(diǎn):
由于在客戶端和真實(shí)主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
實(shí)現(xiàn)代理模式需要額外的工作,有些代理模式的實(shí)現(xiàn)非常復(fù)雜。

  • 單例模式;

1:何為單例模式?
這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時(shí)確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實(shí)例化該類的對象。
注意:
單例類只能有一個實(shí)例。
單例類必須自己創(chuàng)建自己的唯一實(shí)例。
單例類必須給所有其他對象提供這一實(shí)例。
2:如何使用單例模式?
當(dāng)您想控制實(shí)例數(shù)目,節(jié)省系統(tǒng)資源的時(shí)候。
3:單例模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
在內(nèi)存里只有一個實(shí)例,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實(shí)例(比如管理學(xué)院首頁頁面緩存)。
避免對資源的多重占用比如寫文件操作。
缺點(diǎn):
沒有接口,不能繼承,與單一職責(zé)原則沖突,一個類應(yīng)該只關(guān)心內(nèi)部邏輯,而不關(guān)心外面怎么樣來實(shí)例化。

  • 備忘錄模式;

1:何為備忘錄模式?
備忘錄模式(Memento Pattern)保存一個對象的某個狀態(tài),以便在適當(dāng)?shù)臅r(shí)候恢復(fù)對象。備忘錄模式屬于行為型模式。
2:如何使用備忘錄模式?
很多時(shí)候我們總是需要記錄一個對象的內(nèi)部狀態(tài),這樣做的目的就是為了允許用戶取消不確定或者錯誤的操作,能夠恢復(fù)到他原先的狀態(tài),使得他有"后悔藥"可吃。
3:備忘錄模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
給用戶提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個歷史的狀態(tài)。
實(shí)現(xiàn)了信息的封裝,使得用戶不需要關(guān)心狀態(tài)的保存細(xì)節(jié)。
缺點(diǎn):
消耗資源。如果類的成員變量過多,勢必會占用比較大的資源,而且每一次保存都會消耗一定的內(nèi)存。

  • 生成器模式;

1:何為送生成器模式?
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構(gòu)建成一個復(fù)雜的對象。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
2:如何使用生成器模式?
主要解決在軟件系統(tǒng)中,有時(shí)候面臨著"一個復(fù)雜對象"的創(chuàng)建工作,其通常由各個部分的子對象用一定的算法構(gòu)成;由于需求的變化,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩(wěn)定。
一些基本部件不會變,而其組合經(jīng)常變化的時(shí)候。
3:生成器模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
建造者獨(dú)立,易擴(kuò)展。
便于控制細(xì)節(jié)風(fēng)險(xiǎn)。
缺點(diǎn):
產(chǎn)品必須有共同點(diǎn),范圍有限制。
如內(nèi)部變化復(fù)雜,會有很多的建造類。

  • 命令模式;

1:何為命令模式?
命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動的設(shè)計(jì)模式,它屬于行為型模式。請求以命令的形式包裹在對象中,并傳給調(diào)用對象。調(diào)用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應(yīng)的對象,該對象執(zhí)行命令。
主要解決的問題?
在軟件系統(tǒng)中,行為請求者與行為實(shí)現(xiàn)者通常是一種緊耦合的關(guān)系,但某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時(shí),這種無法抵御變化的緊耦合的設(shè)計(jì)就不太合適。
2:如何使用命令模式?
在某些場合,比如要對行為進(jìn)行"記錄、撤銷/重做、事務(wù)"等處理,這種無法抵御變化的緊耦合是不合適的。在這種情況下,如何將"行為請求者"與"行為實(shí)現(xiàn)者"解耦?將一組行為抽象為對象,可以實(shí)現(xiàn)二者之間的松耦合。
3:命令模式的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):降低了系統(tǒng)耦合度,新的命令可以很容易添加到系統(tǒng)中去。
缺點(diǎn):使用命令模式可能會導(dǎo)致某些系統(tǒng)有過多的具體命令類。

8.多線程有哪些?如何保證多線程中讀寫分離,加鎖方案?

NSThread GCD NSOperation

iOS 實(shí)現(xiàn)線程加鎖有很多種方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各種方式。

9.如何刪除單鏈表中一個元素?

先來看看刪除的原理:因?yàn)閿?shù)據(jù)結(jié)構(gòu)是單鏈表,要想刪除第i個節(jié)點(diǎn),就要找到第i個節(jié)點(diǎn);要想找到第i個節(jié)點(diǎn),就要找到第i-1個節(jié)點(diǎn);要想找到第i-1個節(jié)點(diǎn),就要找到第i-2個節(jié)點(diǎn)…于是就要從第一個節(jié)點(diǎn)開始找起,一直找到第i-1個節(jié)點(diǎn)。如何找?讓一個指針從頭結(jié)點(diǎn)開始移動,一直移動到第i-1個節(jié)點(diǎn)為止。這個過程中可以用一個變量j從0開始計(jì)數(shù),一直自增到i-1。之后呢?我們把第i-1個節(jié)點(diǎn)找到了,就讓它的指針域指向第i+1個節(jié)點(diǎn),這樣就達(dá)到了刪除的目的。而第i+1個節(jié)點(diǎn)的地址又從第i個節(jié)點(diǎn)獲得,第i個節(jié)點(diǎn)的地址又是第i-1個節(jié)點(diǎn)的后繼。因此我們可以這樣做:先讓一個指針指向第i-1個節(jié)點(diǎn)的后繼,(保存i+1節(jié)點(diǎn)的地址),再讓i-1節(jié)點(diǎn)的后繼指向第i個節(jié)點(diǎn)的后繼,這樣就將第i個節(jié)點(diǎn)刪除了。(p->next=q->next;)

Status ListDelete(LinkList *L,int i,ElemType *e){
    int j;
    LinkList p,q;
    p = *L; // 聲明一結(jié)點(diǎn)p指向鏈表第一個結(jié)點(diǎn)
    j = 1;
    while (p->next && j < i)  /* 遍歷尋找第i個元素 */
    {
        p = p->next;
        ++j;
    }
    if (!(p->next) || j > i)
        return ERROR;           /* 第i個元素不存在 */
    q = p->next;
    p->next = q->next;            /* 將q的后繼賦值給p的后繼 */
    *e = q->data;               /* 將q結(jié)點(diǎn)中的數(shù)據(jù)給e */
    free(q);                    /* 讓系統(tǒng)回收此結(jié)點(diǎn),釋放內(nèi)存 */
    return OK;
}

10.NSNotificationCenter通知中心的實(shí)現(xiàn)原理?

NSNotificationCenter是類似一個廣播中心站,使用defaultCenter來獲取應(yīng)用中的通知中心,它可以向應(yīng)用任何地方發(fā)送和接收通知。在通知中心注冊觀察者,發(fā)送者使用通知中心廣播時(shí),以NSNotification的name和object來確定需要發(fā)送給哪個觀察者。為保證觀察者能接收到通知,所以應(yīng)先向通知中心注冊觀察者,接著再發(fā)送通知這樣才能在通知中心調(diào)度表中查找到相應(yīng)觀察者進(jìn)行通知。

-(void)postNotification:(NSNotification *)notification;
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

發(fā)送通知通過name和object來確定來標(biāo)識觀察者,name和object兩個參數(shù)的規(guī)則相同即當(dāng)通知設(shè)置name為kChangeNotifition時(shí),那么只會發(fā)送給符合name為kChangeNotifition的觀察者,同理object指發(fā)送給某個特定對象通知,如果只設(shè)置了name,那么只有對應(yīng)名稱的通知會觸發(fā)。如果同時(shí)設(shè)置name和object參數(shù)時(shí)就必須同時(shí)符合這兩個條件的觀察者才能接收到通知。

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);

第一種方式是比較常用的添加Oberver的方式,接到通知時(shí)執(zhí)行aSelector。
第二種方式是基于Block來添加觀察者,往通知中心的調(diào)度表中添加觀察者,這個觀察者包括一個queue和一個block,并且會返回這個觀察者對象。當(dāng)接到通知時(shí)執(zhí)行block所在的線程為添加觀察者時(shí)傳入的queue參數(shù),queue也可以為nil,那么block就在通知所在的線程同步執(zhí)行。
這里需要注意的是如果使用第二種的方式創(chuàng)建觀察者需要弱引用可能引起循環(huán)引用的對象,避免內(nèi)存泄漏。


NSNotificatinonCenter實(shí)現(xiàn)原理:
NSNotificatinonCenter是使用觀察者模式來實(shí)現(xiàn)的用于跨層傳遞消息,用來降低耦合度。
NSNotificatinonCenter用來管理通知,將觀察者注冊到NSNotificatinonCenter的通知調(diào)度表中,然后發(fā)送通知時(shí)利用標(biāo)識符name和object識別出調(diào)度表中的觀察者,然后調(diào)用相應(yīng)的觀察者的方法,即傳遞消息(在Objective-C中對象調(diào)用方法,就是傳遞消息,消息有name或者selector,可以接受參數(shù),而且可能有返回值),如果是基于block創(chuàng)建的通知就調(diào)用NSNotification的block。

11.推送如何實(shí)現(xiàn)的?

  • 1.由App向iOS設(shè)備發(fā)送一個注冊通知,用戶需要同意系統(tǒng)發(fā)送推送。
  • 2.iOS應(yīng)用向APNS遠(yuǎn)程推送服務(wù)器發(fā)送App的Bundle Id和設(shè)備的UDID。
  • 3.APNS根據(jù)設(shè)備的UDID和App的Bundle Id生成deviceToken再發(fā)回給App。
  • 4.App再將deviceToken發(fā)送給遠(yuǎn)程推送服務(wù)器(自己的服務(wù)器), 由服務(wù)器保存在數(shù)據(jù)庫中。
  • 5.當(dāng)自己的服務(wù)器想發(fā)送推送時(shí), 在遠(yuǎn)程推送服務(wù)器中輸入要發(fā)送的消息并選擇發(fā)給哪些用戶的deviceToken,由遠(yuǎn)程推送服務(wù)器發(fā)送給APNS。
  • 6.APNS根據(jù)deviceToken發(fā)送給對應(yīng)的用戶。

12.SEL的使用和原理?

SEL 類成員方法的指針
可以理解 @selector()就是取類方法的編號,他的行為基本可以等同C語言的中函數(shù)指針,只不過C語言中,可以把函數(shù)名直接賦給一個函數(shù)指針,而Object-C的類不能直接應(yīng)用函數(shù)指針,這樣只能做一個@selector語法來取.
它的結(jié)果是一個SEL類型。這個類型本質(zhì)是類方法的編號(函數(shù)地址)

13.點(diǎn)擊事件如何穿透透明的View?

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *hitView = [super hitTest:point withEvent:event];
    if(hitView == self){
        return nil;
    }
    return hitView;
}

14.RunLoop的實(shí)現(xiàn)原理?(答案待完善)

RunLoop實(shí)際上是一個對象,這個對象在循環(huán)中用來處理程序運(yùn)行過程中出現(xiàn)的各種事件(比如說觸摸事件、UI刷新事件、定時(shí)器事件、Selector事件)和消息,從而保持程序的持續(xù)運(yùn)行,而且在沒有事件處理的時(shí)候,會進(jìn)入睡眠模式,從而節(jié)省CPU資源,提高程序性能。

參考:深入理解Runloop

15.簡述Runtime,發(fā)送消息的過程;

動態(tài)的添加對象的成員變量和方法;動態(tài)交換兩個方法的實(shí)現(xiàn);攔截并替換方法;在方法上增加額外功能;實(shí)現(xiàn)NSCoding的自動歸檔和解檔;實(shí)現(xiàn)字典轉(zhuǎn)模型的自動轉(zhuǎn)換;

clang -rewrite-objc main.m 查看最終生成代碼

消息轉(zhuǎn)發(fā):

  • 1:動態(tài)方法解析
+ (BOOL)resolveInstanceMethod:(SEL)selector;
+ (BOOL)resolveClassMethod:(SEL)selector;

  • 2:備援接收者(重定向)
- (id)forwardingTargetForSelector:(SEL)selector;

  • 3:完整的消息轉(zhuǎn)發(fā)(NSInvocation)
- (void)forwardInvocation:(NSInvocation *)invocation;

16.簡述weak的實(shí)現(xiàn)原理;

weak 關(guān)鍵字的作用弱引用,所引用對象的計(jì)數(shù)器不會加一,并在引用對象被釋放的時(shí)候自動被設(shè)置為 nil;

weak是有Runtime維護(hù)的weak表;

3.weak釋放為nil過程
weak被釋放為nil,需要對對象整個釋放過程了解,如下是對象釋放的整體流程:
1、調(diào)用objc_release
2、因?yàn)閷ο蟮囊糜?jì)數(shù)為0,所以執(zhí)行dealloc
3、在dealloc中,調(diào)用了_objc_rootDealloc函數(shù)
4、在_objc_rootDealloc中,調(diào)用了object_dispose函數(shù)
5、調(diào)用objc_destructInstance
6、最后調(diào)用objc_clear_deallocating。

對象準(zhǔn)備釋放時(shí),調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。

其實(shí)Weak表是一個hash(哈希)表,然后里面的key是指向?qū)ο蟮牡刂?,Value是Weak指針的地址的數(shù)組。

總結(jié)

weak是Runtime維護(hù)了一個hash(哈希)表,用于存儲指向某個對象的所有weak指針。weak表其實(shí)是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址(這個地址的值是所指對象指針的地址)數(shù)組。

17.寫一個單例;

+ (instancetype)sharedInstance {
    static RMF *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });

    return instance;
}

18.如何從字符串中得到一個整數(shù)?

- (BOOL)isPureNumandCharacters:(NSString *)text 
{ 
    for(int i = 0; i < [text length]; ++i) {
        int a = [text characterAtIndex:i]; 
        if ([self isNum:a]){
            continue; 
        } else { 
            return NO; 
        } 
    } 
    return YES; 
}

19.數(shù)組去重方式;

  • 數(shù)組法
for (NSString *item in originalArr) {
    if (![resultArrM containsObject:item]) {
      [resultArrM addObject:item];
    }
}

  • 利用NSDictionary
for (NSNumber *n in originalArr) {
    [dict setObject:n forKey:n];
}

  • NSSet
NSSet *set = [NSSet setWithArray:originalArr];

20.設(shè)計(jì)一個數(shù)據(jù)庫;(答案待完善)

主要是對比一下常用的幾種

21.實(shí)現(xiàn)多個網(wǎng)絡(luò)請求ABC執(zhí)行完再執(zhí)行D

方案1:使用group和semaphore
方案2:group_enter和group_leave也可以實(shí)現(xiàn)
下面使用方案1實(shí)現(xiàn)例子

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //異步執(zhí)行A
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });

    dispatch_group_async(group, queue, ^{
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
             //異步執(zhí)行B
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });

    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
             //異步執(zhí)行C
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });

    dispatch_group_notify(group, queue, ^{
             //執(zhí)行D
    });

22.列表頁性能優(yōu)化

  • 如何檢測

1)Instruments中:Core Animation;
2)FPS:CADisplayLink

  • 優(yōu)化方案

1、文本、布局計(jì)算,提前計(jì)算緩存;
2、對象創(chuàng)建;CALayer代替UIView;
3、離屏渲染;
4、圖片解碼;

(離屏渲染是指圖層在被顯示之前是在當(dāng)前屏幕緩沖區(qū)以外開辟的一個緩沖區(qū)進(jìn)行渲染操作。
離屏渲染需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕,而上下文環(huán)境的切換是一項(xiàng)高開銷的動作。)

1.陰影(UIView.layer.shadowOffset/shadowRadius/…)
2.圓角(當(dāng) UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用時(shí))
3.圖層蒙板
4.開啟光柵化(shouldRasterize = true)

1、使用CAShapeLayer和UIBezierPath設(shè)置圓角;
2、UIBezierPath和Core Graphics框架畫出一個圓角;

//1、使用CAShapeLayer和UIBezierPath設(shè)置圓角;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer;

//2、UIBezierPath和Core Graphics框架畫出一個圓角
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.boundscornerRadius:imageView.frame.size.width]addClip];
[imageView drawRect:imageView.bounds];
imageView.image=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];

23.HTTPS(答案待完善)

HTTP+對稱加密和非對稱加密+證書認(rèn)證

23.音視頻相關(guān)

采集視頻,音頻–》使用iOS原生框架 AVFoundation.framework
視頻濾鏡處理–》使用iOS原生框架 CoreImage.framework;使用第三方框架 GPUImage.framework

CoreImage 與 GPUImage 框架比較:
在實(shí)際項(xiàng)目開發(fā)中,開發(fā)者更加傾向使用于GPUImage框架.
首先它在使用性能上與iOS提供的原生框架,并沒有差別;其次它的使用便利性高于iOS原生框架,最后也是最重要的GPUImage框架是開源的.而大家如果想要學(xué)習(xí)GPUImage框架,建議學(xué)習(xí)OpenGL ES,其實(shí)GPUImage的封裝和思維都是基于OpenGL ES.

視頻\音頻編碼壓縮
視頻: 使用FFmpeg,X264算法把視頻原數(shù)據(jù)YUV/RGB編碼成H264
音頻: 使用fdk_aac 將音頻數(shù)據(jù)PCM轉(zhuǎn)換成AAC
視頻: VideoToolBox框架
音頻: AudioToolBox 框架
硬編碼
軟編碼
推流
流媒體協(xié)議: RTMP\RTSP\HLS\FLV
視頻封裝格式: TS\FLV
音頻封裝格式: Mp3\AAC
推流: 將采集的音頻.視頻數(shù)據(jù)通過流媒體協(xié)議發(fā)送到流媒體服務(wù)器
推流技術(shù)
流媒體服務(wù)器
數(shù)據(jù)分發(fā)
截屏
實(shí)時(shí)轉(zhuǎn)碼
內(nèi)容檢測
拉流
拉流: 從流媒體服務(wù)器中獲取音頻\視頻數(shù)據(jù)
流媒體協(xié)議: RTMP\RTSP\HLS\FLV
音視頻解碼
視頻: 使用FFmpeg,X264算法解碼
音頻: 使用fdk_aac 解碼
視頻: VideoToolBox框架
音頻: AudioToolBox 框架
硬解碼
軟解碼
播放
ijkplayer,kxmovie 都是基于FFmpeg框架封裝的
ijkplayer 播放框架
kxmovie 播放框架


??推薦??:

大家可以加入iOS技術(shù)交流圈:937194184; 群內(nèi)提供數(shù)據(jù)結(jié)構(gòu)與算法、底層進(jìn)階、swift、逆向、底層面試題整合文檔等免費(fèi)資料?。。?/strong>


收錄:原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容