iOS | 底層原理分析(二)

一. 多線程

1.1 ios 多線程方案

pthread / NSThread /GCD /NSOperation
image.png

1.2GCD的常用函數(shù)

GCD中有2個用來執(zhí)行任務(wù)的函數(shù)
用同步的方式執(zhí)行任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:隊列
block:任務(wù)

用異步的方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

GCD源碼:https://github.com/apple/swift-corelibs-libdispatch

1.3 GCD的隊列

GCD的隊列可以分為2大類型
并發(fā)隊列(Concurrent Dispatch Queue)
可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

串行隊列(Serial Dispatch Queue)
讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù))

1.4 容易混淆的術(shù)語

有4個術(shù)語比較容易混淆:同步、異步胸哥、并發(fā)稀拐、串行
同步和異步主要影響:能不能開啟新的線程
同步:在當前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務(wù)袖订,具備開啟新線程的能力

并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
并發(fā):多個任務(wù)并發(fā)(同時)執(zhí)行
串行:一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)

1.5 各種隊列的執(zhí)行效果

image.png

1.6 GCD隊列組的使用

image.png

1.7 多線程的安全隱患

資源共享
1塊資源可能會被多個線程共享嗅虏,也就是多個線程可能會訪問同一塊資源
比如多個線程訪問同一個對象洛姑、同一個變量、同一個文件

當多個線程訪問同一塊資源時旋恼,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題
image.png

1.8 多線程安全隱患的解決方案

image.png

1.9 iOS中的線程同步方案 線程安全.線程鎖

解決方案: 使用線程同步技術(shù)(同步,就是協(xié)同步調(diào),按預(yù)定的先后次序進行)
常見線程同步技術(shù): 加鎖

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

1.10 OSSpinLock (自旋鎖) 不安全

image.png

1.11 OSUnfairLock (互斥鎖) 運行效率最高

image.png

1.12 pthread_mutex

image.png

mutex 互斥鎖,等待鎖的線程會處于休眠狀態(tài)

1.13 NSLock吏口、NSRecursiveLock

image.png

1.14 NSCondition

image.png

1.15 dispatch_queue (SerialQueue)

使用GCD串行隊列,實現(xiàn)同步
image.png

1.16 dispatch_semaphore (信號量) 可以用于控制最大并發(fā)數(shù)量

semaphore叫做”信號量”
信號量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量
信號量的初始值為1冰更,代表同時只允許1條線程訪問資源产徊,保證線程同步
image.png

1.17 @synchronized (互斥鎖)

@synchronized是對mutex遞歸鎖的封裝
源碼查看:objc4中的objc-sync.mm文件
@synchronized(obj)內(nèi)部會生成obj對應(yīng)的遞歸鎖,然后進行加鎖蜀细、解鎖操作
image.png

1.18 iOS線程同步方案性能比較

image.png
os_unfair_lock  ios10 開始
OSSpanLock      ios10 廢棄
dispatch_semaphore  
dispatch_mutex
dispatch_queue   串行
NSLock  對 mutex 封裝
@synchronized 最差

1.19 自旋鎖舟铜、互斥鎖比較

什么情況使用自旋鎖比較劃算?
預(yù)計線程等待鎖的時間很短
加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用奠衔,但競爭情況很少發(fā)生
CPU資源不緊張
多核處理器

什么情況使用互斥鎖比較劃算谆刨?
預(yù)計線程等待鎖的時間較長
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
臨界區(qū)競爭非常激烈   

1.20 Atomic 和 Noatomic

atomic用于保證屬性setter、getter的原子性操作归斤,相當于在getter和setter內(nèi)部加了線程同步的鎖
可以參考源碼objc4的objc-accessors.mm
它并不能保證使用屬性的過程是線程安全的

1.21 多線程 讀寫線程安全方案

思考如何實現(xiàn)以下場景
同一時間痊夭,只能有1個線程進行寫的操作
同一時間,允許有多個線程進行讀的操作
同一時間脏里,不允許既有寫的操作她我,又有讀的操作

上面的場景就是典型的“多讀單寫”,經(jīng)常用于文件等數(shù)據(jù)的讀寫操作迫横,iOS中的實現(xiàn)方案有
pthread_rwlock:讀寫鎖
dispatch_barrier_async:異步柵欄調(diào)用

1.22 pthread_rwlock

image.png

1.23 dispatch_barrier_async

image.png


二. 內(nèi)存管理

2.1 CADisplayLink番舆、NSTimer使用注意

    CADisplayLink 保證調(diào)用頻率和刷幀頻率一直,60FPS, 不用設(shè)置時間間隔,每秒鐘60次
    可以使用 proxy 代理解決循環(huán)引用
    
    CADisplayLink、NSTimer會對target產(chǎn)生強引用矾踱,如果target又對它們產(chǎn)生強引用恨狈,那么就會引發(fā)循環(huán)引用

解決方案1.使用block


image.png

解決方案2.使用代理對象(NSProxy)


image.png

2.2 NSProxy 也屬于基類

代理,用于解決循環(huán)引用,,用于消息轉(zhuǎn)發(fā),不會在父類查找方法
NSObject 和 NSProxy 區(qū)別

2.3 GCD定時器

NSTimer依賴于RunLoop,如果RunLoop的任務(wù)過于繁重呛讲,可能會導(dǎo)致NSTimer不準時
而GCD的定時器會更加準時,GCD定時器,不依賴 Runloop ,會很準時,依賴內(nèi)核
image.png

2.4 iOS 程序的內(nèi)存布局

低地址-> 高地址
保留->代碼段->數(shù)據(jù)段(字符串常量,已初始化全局數(shù)據(jù),未初始化數(shù)據(jù))>堆->棧內(nèi)存-> 內(nèi)核區(qū)域
代碼段: 編譯之后的代碼
數(shù)據(jù)段: 字符串常量,已經(jīng)初始化的全局變量,或者靜態(tài)變量,未初始化的全局變量,靜態(tài)變量
堆 (低>高)  通過 alloc malloc calloc 動態(tài)分配的內(nèi)存

棧 (高地址 從 低地址)  函數(shù)調(diào)用開銷()
image.png

2.5 Tagged Pointer

從64bit開始禾怠,iOS引入了Tagged Pointer技術(shù),用于優(yōu)化NSNumber圣蝎、NSDate刃宵、NSString等小對象的存儲

在沒有使用Tagged Pointer之前, NSNumber等對象需要動態(tài)分配內(nèi)存徘公、維護引用計數(shù)等,NSNumber指針存儲的是堆中NSNumber對象的地址值

使用Tagged Pointer之后关面,NSNumber指針里面存儲的數(shù)據(jù)變成了:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中

當指針不夠存儲數(shù)據(jù)時,才會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)

objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法,直接從指針提取數(shù)據(jù),節(jié)省了以前的調(diào)用開銷

如何判斷一個指針是否為Tagged Pointer?
iOS平臺,最高有效位是1(第64bit)
Mac平臺潜沦,最低有效位是1

判斷是否為Tagged Pointer

image.png

2.6 OC對象的內(nèi)存管理

在iOS中枣察,使用引用計數(shù)來管理OC對象的內(nèi)存

一個新創(chuàng)建的OC對象引用計數(shù)默認是1臂痕,當引用計數(shù)減為0叛赚,OC對象就會銷毀溪掀,釋放其占用的內(nèi)存空間

調(diào)用retain會讓OC對象的引用計數(shù)+1氛琢,調(diào)用release會讓OC對象的引用計數(shù)-1

內(nèi)存管理的經(jīng)驗總結(jié)
當調(diào)用alloc、new障般、copy、mutableCopy方法返回了一個對象藐石,在不需要這個對象時定拟,要調(diào)用release或者autorelease來釋放它
想擁有某個對象于微,就讓它的引用計數(shù)+1青自;不想再擁有某個對象,就讓它的引用計數(shù)-1

可以通過以下私有函數(shù)來查看自動釋放池的情況
extern void _objc_autoreleasePoolPrint(void);

2.7 copy和mutableCopy

image.png

2.8 引用計數(shù)器的存儲 retaincount

image.png

2.9 dealloc

image.png

2.10 autoreleasePool 自動釋放池

自動釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是:__AtAutoreleasePool获高、AutoreleasePoolPage

調(diào)用了autorelease的對象最終都是通過AutoreleasePoolPage對象來管理的

源碼分析
-clang重寫@autoreleasepool
-objc4源碼:NSObject.mm
image.png

2.11 AutoreleasePoolPage的結(jié)構(gòu)

image.png
調(diào)用push方法會將一個POOL_BOUNDARY入棧,并且返回其存放的內(nèi)存地址

調(diào)用pop方法時傳入一個POOL_BOUNDARY的內(nèi)存地址念秧,會從最后一個入棧的對象開始發(fā)送release消息淤井,直到遇到這個POOL_BOUNDARY

id *next指向了下一個能存放autorelease對象地址的區(qū)域

2.12 runloop 和 autoreleasePool

iOS在主線程的Runloop中注冊了2個Observer
-第1個Observer監(jiān)聽了kCFRunLoopEntry事件,會調(diào)用objc_autoreleasePoolPush()
-第2個Observer
    監(jiān)聽了kCFRunLoopBeforeWaiting事件,會調(diào)用objc_autoreleasePoolPop()庄吼、objc_autoreleasePoolPush()
    監(jiān)聽了kCFRunLoopBeforeExit事件缎除,會調(diào)用objc_autoreleasePoolPop()

三 . 性能優(yōu)化

3.1 CPU和GPU

image.png

3.2 卡頓產(chǎn)生的原因

image.png

3.3 卡頓優(yōu)化-CPU

image.png

3.4 卡頓優(yōu)化 - GPU

image.png

3.5 離屏渲染

image.png

3.6 卡頓檢查

平時所說的“卡頓”主要是因為在主線程執(zhí)行了比較耗時的操作

可以添加Observer到主線程RunLoop中严就,通過監(jiān)聽RunLoop狀態(tài)切換的耗時图甜,以達到監(jiān)控卡頓的目的

3.7 耗電的主要來源

image.png

3.8 耗電優(yōu)化

image.png
image.png

3.9 App 啟動

image.png
image.png

APP的啟動 - dyld


image.png

APP的啟動 - runtime


image.png

APP的啟動 - main


image.png

3.10 APP的啟動優(yōu)化

image.png

3.11 安裝包瘦身

image.png
image.png


四 . 設(shè)計模式與架構(gòu)

4.1 何為架構(gòu)次员?

image.png

4.2 MVC - Apple版

model-view-controller


image.png

4.3 MVC - 變種

image.png

4.4 MVP

model-view-presenter


image.png

4.5 MVVM

model - view - viewModel


image.png

4.6 三層架構(gòu)

應(yīng)用層/界面層
業(yè)務(wù)層
數(shù)據(jù)層

4.7 四層架構(gòu)

應(yīng)用層/界面層
業(yè)務(wù)層
網(wǎng)絡(luò)層
數(shù)據(jù)層


image.png

4.8 設(shè)計模式

image.png


iOS底層面試知識點總結(jié)

1. 一個OC對象占用多少內(nèi)存

系統(tǒng)分配了16個字節(jié)給NSObject對象(通過malloc_size函數(shù)獲得)
但NSObject對象內(nèi)部只使用了8個字節(jié)的空間(64bit環(huán)境下,可以通過class_getInstanceSize函數(shù)獲得)

2. 對象的isa指針指向哪里?

instance對象的isa指向class對象
class對象的isa指向meta-class對象
meta-class對象的isa指向基類的meta-class對象

3.OC的類信息存放在哪里吱晒?

對象方法、屬性灭贷、成員變量削茁、協(xié)議信息,存放在class對象中
類方法粟害,存放在meta-class對象中
成員變量的具體值蕴忆,存放在instance對象

4.iOS用什么方式實現(xiàn)對一個對象的KVO?(KVO的本質(zhì)是什么悲幅?)

- 利用RuntimeAPI動態(tài)生成一個子類套鹅,并且讓instance對象的isa指向這個全新的子類
- 當修改instance對象的屬性時,會調(diào)用Foundation的_NSSetXXXValueAndNotify函數(shù)
    willChangeValueForKey:
    父類原來的setter
    didChangeValueForKey:
- 內(nèi)部會觸發(fā)監(jiān)聽器(Oberser)的監(jiān)聽方法(observeValueForKeyPath:ofObject:change:context:)

5.如何手動觸發(fā)KVO汰具?

手動調(diào)用willChangeValueForKey:和didChangeValueForKey:
//
- (void)viewDidLoad {
[super viewDidLoad];
    
    Person *person = [[Person alloc]init];;
    [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    [p willChangeValueForKey:@"name"];
    [p didChangeValueForKey:@"name"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"被觀測對象:%@, 被觀測的屬性:%@, 值的改變: %@\n, 攜帶信息:%@", object, keyPath, change, context);
}

6.直接修改成員變量會觸發(fā)KVO么卓鹿?

不會觸發(fā)KVO

7.通過KVC修改屬性會觸發(fā)KVO么?

會觸發(fā)KVO
KVC在賦值時候,內(nèi)部會觸發(fā)監(jiān)聽器(Oberser)的監(jiān)聽方法(observeValueForKeyPath:ofObject:change:context:) 發(fā)送通知

8.KVC的賦值和取值過程是怎樣的留荔?原理是什么吟孙?

KVC的全稱是Key-Value Coding,俗稱“鍵值編碼”聚蝶,可以通過一個key來訪問某個屬性
調(diào)用 setValue:forKey:
setKey,_setKey  ->找到了則進行賦值,未找到調(diào)用 accessInstanceVarlableDirctly 是否運行 修改值,返回YES
調(diào)用_key, _isKey, key, isKey 進行賦值

9.Category的使用場合是什么杰妓?

- 在不修改原有類代碼的情況下,為類添對象方法或者類方法
- 或者為類關(guān)聯(lián)新的屬性
- 分解龐大的類文件

使用場合:
- 添加實例方法
- 添加類方法
- 添加協(xié)議
- 添加屬性
- 關(guān)聯(lián)成員變量

10.Category的實現(xiàn)原理

Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲著分類的對象方法碘勉、類方法巷挥、屬性、協(xié)議信息
在程序運行的時候恰聘,runtime會將Category的數(shù)據(jù)句各,合并到類信息中(類對象、元類對象中)

11.Category和Class Extension的區(qū)別是什么晴叨?

Class Extension在編譯的時候凿宾,它的數(shù)據(jù)就已經(jīng)包含在類信息中
Category是在運行時,才會將數(shù)據(jù)合并到類信息中

12.Category中有l(wèi)oad方法嗎兼蕊?load方法是什么時候調(diào)用的初厚?load 方法能繼承嗎?

- 有l(wèi)oad方法
- load方法在runtime加載類、分類的時候調(diào)用
- load方法可以繼承产禾,但是一般情況下不會主動去調(diào)用load方法排作,都是讓系統(tǒng)自動調(diào)用

13. initialize方法如何調(diào)用,以及調(diào)用時機

- 當類第一次收到消息的時候會調(diào)用類的initialize方法
- 是通過 runtime 的消息機制 objc_msgSend(obj,@selector()) 進行調(diào)用的
- 優(yōu)先調(diào)用分類的 initialize, 如果沒有分類會調(diào)用 子類的,如果子類未實現(xiàn)則調(diào)用 父類的

13. load、initialize方法的區(qū)別什么亚情?它們在category中的調(diào)用的順序妄痪?以及出現(xiàn)繼承時他們之間的調(diào)用過程?

- load 是類加載到內(nèi)存時候調(diào)用, 優(yōu)先父類->子類->分類
- initialize 是類第一次收到消息時候調(diào)用,優(yōu)先分類->子類->父類
- 同級別和編譯順序有關(guān)系
- load 方法是在 main 函數(shù)之前調(diào)用的

14. Category能否添加成員變量楞件?如果可以衫生,如何給Category添加成員變量?

不能直接給Category添加成員變量土浸,但是可以間接實現(xiàn)Category有成員變量的效果
Category是發(fā)生在運行時,編譯完畢,類的內(nèi)存布局已經(jīng)確定,無法添加成員變量(Category的底層數(shù)據(jù)結(jié)構(gòu)也沒有成員變量的結(jié)構(gòu))
可以通過 runtime 動態(tài)的關(guān)聯(lián)屬性

15. block的原理是怎樣的罪针?本質(zhì)是什么?

block 本質(zhì)其實是OC對象
block 內(nèi)部封裝了函數(shù)調(diào)用以及調(diào)用環(huán)境

16. __block的作用是什么黄伊?有什么使用注意點泪酱?

如果需要在 block 內(nèi)部修改外部的 局部變量的值,就需要使用__block 修飾(全局變量和靜態(tài)變量不需要加__block 可以修改)

__block 修飾以后,局部變量的數(shù)據(jù)結(jié)構(gòu)就會發(fā)生改變,底層會變成一個結(jié)構(gòu)體的對象,結(jié)構(gòu)內(nèi)部會聲明 一個 __block修飾變量的成員, 并且將 __block修飾變量的地址保存到堆內(nèi)存中. 后面如果修改 這個變量的值,可以通過 isa 指針找到這個結(jié)構(gòu)體,進來修改 這個變量的值;

可以在 block 內(nèi)部修改 變量的值

17. block的屬性修飾詞為什么是copy?使用block有哪些使用注意还最?

block 一旦沒有進行copy操作墓阀,就不會在堆上
使用注意:循環(huán)引用問題 (外部使用__weak 解決)

17. block在修改NSMutableArray,需不需要添加__block憋活?

如果是操作 NSMutableArray 對象不需要,因為 block 內(nèi)部拷貝了 NSMutableArray對象的內(nèi)存地址,實際是通過內(nèi)存地址操作的
如果 NSMutableArray 對象要重新賦值,就需要加__block

18. Block 內(nèi)部為什么不能修改局部變量,需要加__block

通過查看Block 源碼,可以發(fā)現(xiàn), block 內(nèi)部如果單純使用 外部變量, 會在 block 內(nèi)部創(chuàng)建同樣的一個變量,并且將 外部變量的值引用過來..(只是將外部變量值拷貝到 block 內(nèi)部), 內(nèi)部這個變量和外部 實際已經(jīng)沒關(guān)系了

從另一方面分析,block 本質(zhì)也是一個 函數(shù)指針, 外部的變量也是一個局部變量,很有可能 block 在使用這個變量時候,外部變量已經(jīng)釋放了,會造成錯誤

加了__block 以后, 會將外部變量的內(nèi)存拷貝到堆中, 內(nèi)存由 block 去管理.

19.講一下 OC 的消息機制

OC中的方法調(diào)用其實都是轉(zhuǎn)成了objc_msgSend函數(shù)的調(diào)用岂津,給receiver(方法調(diào)用者)發(fā)送了一條消息(selector方法名)
objc_msgSend底層有3大階段
消息發(fā)送(當前類、父類中查找)悦即、動態(tài)方法解析吮成、消息轉(zhuǎn)發(fā)
19.1 消息發(fā)送流程
當我們的一個 receiver(實例對象)收到消息的時候, 會通過 isa 指針找到 他的類對象, 然后在類對象方法列表中查找 對應(yīng)的方法實現(xiàn),如果 未找到,則會通過 superClass 指針找到其父類的類對象, 找到則返回,未找打則會一級一級往上查到,最終到NSObject 對象, 如果還是未找到就會進行動態(tài)方法解析

類方法調(diào)用同上,只不過 isa 指針找到元類對象;
19.1 動態(tài)方法解析機制
當我們發(fā)送消息未找到方法實現(xiàn),就會進入第二步,動態(tài)方法解析: 代碼實現(xiàn)如下

//  動態(tài)方法綁定- 實例法法調(diào)用
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(run)) {
        Method method = class_getInstanceMethod(self, @selector(test));
        class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

// 類方法調(diào)用
+(BOOL) resolveClassMethod:(SEL)sel....

20.消息轉(zhuǎn)發(fā)機制流程

未找到動態(tài)方法綁定,就會進行消息轉(zhuǎn)發(fā)階段

// 快速消息轉(zhuǎn)發(fā)- 指定消息處理對象
- (id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(run)) {
        return [Student new];
    }
    return  [super forwardingTargetForSelector:aSelector];
} 

// 標準消息轉(zhuǎn)發(fā)-消息簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if(aSelector == @selector(run))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
   //內(nèi)部邏輯自己處理 
}

21.什么是Runtime?平時項目中有用過么辜梳?

Objective-C runtime是一個`運行時`庫粱甫,它為Objective-C語言的動態(tài)特性提供支持,我們所寫的OC代碼在運行時都轉(zhuǎn)成了runtime相關(guān)的代碼作瞄,類轉(zhuǎn)換成C語言對應(yīng)的結(jié)構(gòu)體茶宵,方法轉(zhuǎn)化為C語言對應(yīng)的函數(shù),發(fā)消息轉(zhuǎn)成了C語言對應(yīng)的函數(shù)調(diào)用宗挥。通過了解runtime以及源碼,可以更加深入的了解OC其特性和原理

OC是一門動態(tài)性比較強的編程語言乌庶,允許很多操作推遲到程序運行時再進行
OC的動態(tài)性就是由Runtime來支撐和實現(xiàn)的,Runtime是一套C語言的API契耿,封裝了很多動態(tài)性相關(guān)的函數(shù)
平時編寫的OC代碼瞒大,底層都是轉(zhuǎn)換成了Runtime API進行調(diào)用

22.runtime具體應(yīng)用

利用關(guān)聯(lián)對象(AssociatedObject)給分類添加屬性
遍歷類的所有成員變量(修改textfield的占位文字顏色、字典轉(zhuǎn)模型搪桂、自動歸檔解檔)
交換方法實現(xiàn)(交換系統(tǒng)的方法)
利用消息轉(zhuǎn)發(fā)機制解決方法找不到的異常問題

23.打印結(jié)果分別是什么透敌?

image.png
[self class] 和 [super class] 都是給當前類返送消息,spuer 表示在父類中查找
[self superClass]  和 [super superclass] 也是也當前類發(fā)消息,返回父類
第一個打印:
MJStudent / MJStudent/ MJerson / MJPerson

isKindOfClass 表示對象是否為當前類或者子類的 類型
isMemberOfClass 表示是否為當前類的的類型
isMemberOfClass 分為- 對象方法 和+ 類方法2中
- (bool)isMemberOfClass; 比較的是類對象
+ (bool)isMemberOfClass; 比較的是元類
第二個打印:
1 ,0, 0, 0

24.以下代碼能不能執(zhí)行成功?如果可以,打印結(jié)果是什么酗电?

image.png
打印結(jié)果: <ViewController: 0x7f9396c16300>

25.講講 RunLoop魄藕,項目中有用到嗎?

runloop運行循環(huán),保證程序一直運行,主線程默認開啟
用于處理線程上的各種事件,定時器等
可以提高程序性能,節(jié)約CPU資源,有事情做就做,沒事情做就讓線程休眠

應(yīng)用范疇:
定時器,事件響應(yīng),手勢識別,界面刷新,以及autoreleasePool 等等

26.runloop內(nèi)部實現(xiàn)邏輯撵术?

image.png
實際上 RunLoop 就是這樣一個函數(shù)背率,其內(nèi)部是一個 do-while 循環(huán)。當你調(diào)用 CFRunLoopRun() 時荷荤,線程就會一直停留在這個循環(huán)里退渗;直到超時或被手動停止,該函數(shù)才會返回蕴纳。

27.runloop和線程的關(guān)系?

每條線程都有唯一的一個與之對應(yīng)的RunLoop對象
RunLoop保存在一個全局的Dictionary里个粱,線程作為key古毛,RunLoop作為value
線程剛創(chuàng)建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建
RunLoop會在線程結(jié)束時銷毀
主線程的RunLoop已經(jīng)自動獲榷夹怼(創(chuàng)建)稻薇,子線程默認沒有開啟RunLoop

28.timer 與 runloop 的關(guān)系?

timer 定時器,是基于 runloop 來實現(xiàn)的, runloop 在運行循環(huán)當中,監(jiān)聽到了定制器 就會執(zhí)行;所以 timer 需要添加到 runloop 中去, 注意子線程的 runloop 默認是不開啟的,如果在子線程執(zhí)行 timer 需要手動開啟 runloop

29.程序中添加每3秒響應(yīng)一次的NSTimer胶征,當拖動tableview時timer可能無法響應(yīng)要怎么解決塞椎?

將 timer 對象添加到 runloop 中,并修改 runloop 的運行 mode

 NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:nil];
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

30.runloop 是怎么響應(yīng)用戶操作的, 具體流程是什么樣的睛低?

不明白問題想問什么?

31.說說runLoop的幾種狀態(tài)

添加Observer監(jiān)聽RunLoop的所有狀態(tài)


image.png

32.runloop的mode作用是什么案狠?

runloop 只能在一種 mode 下運行, 做不同的事情,runloop 會切換到對應(yīng)的 model 下來執(zhí)行,默認是  kCFRunLoopDefaultMode 如果視圖滑動再回切換到  UITrackingRunLoopMode,如果需要在多種 mode 下運行則需要手動設(shè)置 kCFRunLoopCommonModes;


1. kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行
2. UITrackingRunLoopMode:界面跟蹤 Mode钱雷,用于 ScrollView 追蹤觸摸滑動骂铁,保證界面滑動時不受其他 Mode 影響
3. UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用罩抗,會切換到kCFRunLoopDefaultMode
4. GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode拉庵,通常用不到
5. kCFRunLoopCommonModes: 這是一個占位用的Mode,作為標記kCFRunLoopDefaultMode和UITrackingRunLoopMode用套蒂,并不是一種真正的Mode 

33.你理解的多線程钞支?

同一時間,CPU 只能處理理一條線程, 只有一條線程在?工作 多線程并發(fā)執(zhí)行,其實是 CPU 快速的在多條線程之間調(diào)度(切換) 如果 CPU 調(diào)度線程的時間?足夠快, 就造成了多線程并發(fā)執(zhí)?的假象

優(yōu)勢
充分發(fā)揮多核處理器的優(yōu)勢,將不同線程任務(wù)分配給不同的處理器操刀,真正進入“?行 計算”狀態(tài)
弊端 
新線程會消耗內(nèi)存控件和cpu時間烁挟,線程太多會降低系統(tǒng)行性能。

34.iOS的多線程方案有哪幾種馍刮?你更傾向于哪一種信夫?

image.png
傾向于GCD ,簡單靈活,使用方便

35.你在項目中用過 GCD 嗎?

使用過

GCD中有2個用來執(zhí)行任務(wù)的函數(shù)
用同步的方式執(zhí)行任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:隊列
block:任務(wù)

用異步的方式執(zhí)行任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

36.GCD 的隊列類型

GCD的隊列可以分為2大類型
并發(fā)隊列(Concurrent Dispatch Queue)
可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

串行隊列(Serial Dispatch Queue)
讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù))

37.說一下 OperationQueue 和 GCD 的區(qū)別静稻,以及各自的優(yōu)勢

1> GCD是純C語?言的API警没,NSOperationQueue是基于GCD的OC版本封裝
2> GCD只?支持FIFO的隊列列,NSOperationQueue可以很?方便便地調(diào)整執(zhí)?行行順 序振湾、設(shè) 置最?大并發(fā)數(shù)量量
3> NSOperationQueue可以在輕松在Operation間設(shè)置依賴關(guān)系杀迹,?而GCD 需要寫很 多的代碼才能實現(xiàn)
4> NSOperationQueue?支持KVO,可以監(jiān)測operation是否正在執(zhí)?行行 (isExecuted)押搪、 是否結(jié)束(isFinished)树酪,是否取消(isCanceld)
5> GCD的執(zhí)?行行速度?比NSOperationQueue快 任務(wù)之間不不太互相依賴:GCD 任務(wù)之間 有依賴\或者要監(jiān)聽任務(wù)的執(zhí)?行行情況:NSOperationQueue

38.線程安全的處理手段有哪些?

1.加鎖
2.同步執(zhí)行

39.OC你了解的鎖有哪些大州?在你回答基礎(chǔ)上進行二次提問续语;

os_unfair_lock  ios10 開始
OSSpanLock      ios10 廢棄
dispatch_semaphore   建議使用,性能也比較好
dispatch_mutex
dispatch_queue   串行
NSLock  對 mutex 封裝
@synchronized 性能最差

40.追問一:自旋和互斥對比?

什么情況使用自旋鎖比較劃算厦画?
預(yù)計線程等待鎖的時間很短
加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用疮茄,但競爭情況很少發(fā)生
CPU資源不緊張
多核處理器

什么情況使用互斥鎖比較劃算?
預(yù)計線程等待鎖的時間較長
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
臨界區(qū)競爭非常激烈   

41.追問二:使用以上鎖需要注意哪些根暑?

注意死鎖
在串行隊列使用同步,容易造成死鎖

42.追問三:用C/OC/C++力试,任選其一,實現(xiàn)自旋或互斥排嫌?口述即可畸裳!

兩種鎖的加鎖原理:

互斥鎖:線程會從sleep(加鎖)——>running(解鎖),過程中有上下文的切換淳地,cpu的搶占怖糊,信號的發(fā)送等開銷。

自旋鎖:線程一直是running(加鎖——>解鎖)薇芝,死循環(huán)檢測鎖的標志位蓬抄,

43.請問下面代碼的打印結(jié)果是什么?

image.png
打印 1,3
performSelector after 是基于 timer 定制器,定時器又是基于 runloop 實現(xiàn)的
任務(wù)2在子線程中,子線程默認 runloop 是不開啟的,所以不執(zhí)行2

44.請問下面代碼的打印結(jié)果是什么夯到?

image.png
打印1
start 執(zhí)行完,線程就銷毀了.任務(wù) test 沒法執(zhí)行了

45.使用CADisplayLink嚷缭、NSTimer有什么注意點?

CADisplayLink 保證調(diào)用頻率和刷幀頻率一直,60FPS, 不用設(shè)置時間間隔,每秒鐘60次
    可以使用 proxy 代理解決循環(huán)引用
    
    CADisplayLink耍贾、NSTimer會對target產(chǎn)生強引用阅爽,如果target又對它們產(chǎn)生強引用,那么就會引發(fā)循環(huán)引用

46.介紹下內(nèi)存的幾大區(qū)域

低地址-> 高地址
保留->代碼段->數(shù)據(jù)段(字符串常量,已初始化全局數(shù)據(jù),未初始化數(shù)據(jù))>堆->棧內(nèi)存-> 內(nèi)核區(qū)域
代碼段: 編譯之后的代碼
數(shù)據(jù)段: 字符串常量,已經(jīng)初始化的全局變量,或者靜態(tài)變量,未初始化的全局變量,靜態(tài)變量
堆 (低>高)  通過 alloc malloc calloc 動態(tài)分配的內(nèi)存

棧 (高地址 從 低地址)  函數(shù)調(diào)用開銷()

47.講一下你對 iOS 內(nèi)存管理的理解

在iOS中荐开,使用引用計數(shù)來管理OC對象的內(nèi)存

一個新創(chuàng)建的OC對象引用計數(shù)默認是1付翁,當引用計數(shù)減為0,OC對象就會銷毀晃听,釋放其占用的內(nèi)存空間

調(diào)用retain會讓OC對象的引用計數(shù)+1百侧,調(diào)用release會讓OC對象的引用計數(shù)-1

內(nèi)存管理的經(jīng)驗總結(jié)
當調(diào)用alloc砰识、new、copy佣渴、mutableCopy方法返回了一個對象辫狼,在不需要這個對象時,要調(diào)用release或者autorelease來釋放它
想擁有某個對象辛润,就讓它的引用計數(shù)+1膨处;不想再擁有某個對象,就讓它的引用計數(shù)-1

可以通過以下私有函數(shù)來查看自動釋放池的情況
extern void _objc_autoreleasePoolPrint(void);

48.ARC 都幫我們做了什么砂竖?

LLVM + Runtime 會為我們代碼自動插入 retain 和 release 以及 autorelease等代碼,不需要我們手動管理

49.weak指針的實現(xiàn)原理

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

runtime對注冊的類三圆, 會進行布局狞换,對于weak對象會放入一個hash表中。 用weak指向的對象內(nèi)存地址作為key舟肉,當此對象的引用計數(shù)為0的時候會dealloc,假如weak指向的對象內(nèi)存地址是a查库,那么就會以a為鍵路媚, 在這個weak表中搜索,找到所有以a為鍵的weak對象樊销,從而設(shè)置為nil整慎。

50.autorelease對象在什么時機會被調(diào)用release

iOS在主線程的Runloop中注冊了2個Observer
-第1個Observer監(jiān)聽了kCFRunLoopEntry事件,會調(diào)用objc_autoreleasePoolPush()
-第2個Observer
    監(jiān)聽了kCFRunLoopBeforeWaiting事件围苫,會調(diào)用objc_autoreleasePoolPop()裤园、objc_autoreleasePoolPush()
    監(jiān)聽了kCFRunLoopBeforeExit事件,會調(diào)用objc_autoreleasePoolPop()
    
    objc_autoreleasePoolPop()調(diào)用時候回給 pool 中的對象發(fā)送一次 release 消息

51.方法里有局部對象剂府, 出了方法后會立即釋放嗎

如果是普通的 局部對象 會立即釋放
如果是放在了 autoreleasePool 自動釋放吃,則會等runloop 循環(huán),進入休眠前釋放

52.思考以下2段代碼能發(fā)生什么事拧揽?有什么區(qū)別?

image.png
第一個內(nèi)存會暴漲,self.name 會不行的創(chuàng)建
第二個內(nèi)存固定,會使用 Tagged Pointer 將值存在地址中

53.你在項目中是怎么優(yōu)化內(nèi)存的腺占?

內(nèi)存優(yōu)化可以從 內(nèi)存泄漏 和 內(nèi)存開銷 2方面入口

- 減少內(nèi)存泄露
  可以使用靜態(tài)分析以及instruments的leaks 分析
  注意 NStimer 以及 block ,delegate 等的使用,避免循環(huán)引用
   
   
- 降低內(nèi)存使用峰值
  1. 關(guān)于圖片加載占用內(nèi)存問題:imageNamed: 方法會在內(nèi)存中緩存圖片淤袜,用于常用的圖片。
   imageWithContentsOfFile: 方法在視圖銷毀的時候會釋放圖片占用的內(nèi)存衰伯,適合不常用的大圖等铡羡。
   
  2.tableView cell 盡量使用重用機制,減少額外的開銷
  3.tableView 列表圖片展示盡量使用縮略圖
  4.延遲加載 對象,節(jié)約內(nèi)存開銷
  5.避免短時間大量創(chuàng)建對象,配合 autoreleasePool 減少內(nèi)存峰值
  6.重用大開銷對象,比如: NSDateFormatter和NSCalendar
  7.加載 html 盡量使用 wkwebView
  8.單例使用不易過多
  9.線程最大并發(fā)數(shù)

54.優(yōu)化你是從哪幾方面著手?

卡頓優(yōu)化
啟動優(yōu)化
耗電量優(yōu)化
app 瘦身

CPU 占用率意鲸、 內(nèi)存使用情況烦周、網(wǎng)絡(luò)狀況監(jiān)控尽爆、啟動時閃退、卡頓读慎、FPS漱贱、使用時崩潰、耗電量監(jiān)控贪壳、流量監(jiān)控....

55.列表卡頓的原因可能有哪些饱亿?你平時是怎么優(yōu)化的?

1.最常用的就是cell的重用闰靴, 注冊重用標識符
   如果不重用cell時彪笼,每當一個cell顯示到屏幕上時,就會重新創(chuàng)建一個新的cell蚂且;
   如果有很多數(shù)據(jù)的時候配猫,就會堆積很多cell。
   如果重用cell杏死,為cell創(chuàng)建一個ID泵肄,每當需要顯示cell 的時候,都會先去緩沖池中尋找可循環(huán)利用的cell淑翼,如果沒有再重新創(chuàng)建cell
   
2.避免cell的重新布局
   cell的布局填充等操作 比較耗時腐巢,一般創(chuàng)建時就布局好
   如可以將cell單獨放到一個自定義類,初始化時就布局好
   
3.提前計算并緩存cell的屬性及內(nèi)容
    當我們創(chuàng)建cell的數(shù)據(jù)源方法時玄括,編譯器并不是先創(chuàng)建cell 再定cell的高度
    而是先根據(jù)內(nèi)容一次確定每一個cell的高度冯丙,高度確定后,再創(chuàng)建要顯示的cell遭京,滾動時胃惜,每當cell進入憑虛都會計算高度,提前估算高度告訴編譯器哪雕,編譯器知道高度后船殉,緊接著就會創(chuàng)建cell,這時再調(diào)用高度的具體計算方法斯嚎,這樣可以方式浪費時間去計算顯示以外的cell
    
4.減少cell中控件的數(shù)量
   盡量使cell得布局大致相同利虫,不同風格的cell可以使用不用的重用標識符,初始化時添加控件孝扛,
   不適用的可以先隱藏
   
5.不要使用ClearColor列吼,無背景色,透明度也不要設(shè)置為0
   渲染耗時比較長
   
6.使用局部更新
   如果只是更新某組的話苦始,使用reloadSection進行局部更新
   
7.加載網(wǎng)絡(luò)數(shù)據(jù)寞钥,下載圖片,使用異步加載陌选,并緩存

8.少使用addView 給cell動態(tài)添加view

9.按需加載cell理郑,cell滾動很快時蹄溉,只加載范圍內(nèi)的cell

10.不要實現(xiàn)無用的代理方法,tableView只遵守兩個協(xié)議

11.緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時存在您炉,這兩者同時存在才會出現(xiàn)“竄動”的bug柒爵。所以我的建議是:只要是固定行高就寫預(yù)估行高來減少行高調(diào)用次數(shù)提升性能。如果是動態(tài)行高就不要寫預(yù)估方法了赚爵,用一個行高的緩存字典來減少代碼的調(diào)用次數(shù)即可

12.不要做多余的繪制工作棉胀。在實現(xiàn)drawRect:的時候,它的rect參數(shù)就是需要繪制的區(qū)域冀膝,這個區(qū)域之外的不需要進行繪制唁奢。例如上例中,就可以用CGRectIntersectsRect窝剖、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text麻掸,然后再調(diào)用繪制方法。

13.預(yù)渲染圖像赐纱。當新的圖像出現(xiàn)時脊奋,仍然會有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫一遍疙描,導(dǎo)出成UIImage對象诚隙,然后再繪制到屏幕;

14.使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)起胰。

56.app 啟動優(yōu)化

1. pre-main 之前
    
    * 排查無用的動態(tài)庫(定期清理)
    * 減少ObjC類(項目中不適用的的庫最楷,廢棄的代碼等)、方法(selector)待错、分類(category)的數(shù)量、無用的庫
    * 少在類的+load方法里做事情烈评,盡量把這些事情推遲到+initiailize1.
    
2. main 函數(shù)之后的 didFinishLaunchingWithOptions 加載完之前
    
    * 不影響用戶體驗的操作,做延遲加載,不要全部放在  didFinishLaunchingWithOptions中去做
    * 版本更新,一些三方初始化,不需要在 didFinishLaunchingWithOptions 初始化的放到,界面展示完以后再初始化
    * 一些網(wǎng)絡(luò)請求延遲 請求..
    * 一些業(yè)務(wù)邏輯延遲 加載
    * 初始化第三方 SDK
    * 配置 APP 運行需要的環(huán)境
    * 自己的一些工具類的初始化

57.app 耗電量優(yōu)化

1.不要頻繁的刷新頁面,能刷新1行cell最好只刷新一行,盡量不要使用reloadData.
2.選擇正確的集合
    NSArray,使用index來查找很快(插入和刪除很慢)
    字典,使用鍵來查找很快
    NSSets,是無序的,用鍵查找很快,插入/刪除很快
3.少用運算獲得圓角,必須要用圓角的話,不如把圖片本身就做成圓角
4.懶加載,不要一次性創(chuàng)建所有的subview,而是需要時才創(chuàng)建.
5.重用機制
6.圖片處理
    圖片與imageView相同大小,避免多余運算
    可以使用整副的圖片,增加應(yīng)用體積,但是節(jié)省CPU
    可調(diào)大小的圖片,可以省去一些不必要的空間
    CALayer,CoreGraphics,甚至OpenGL來繪制,消耗CPU
7.cache,cache,cache(緩存所有需要的)
    服務(wù)器相應(yīng)結(jié)果的緩存(圖片)
    復(fù)雜計算結(jié)果的緩存(UITableView的行高)
8.盡量少用透明或半透明,會產(chǎn)生額外的運算.

9.使用ARC減少內(nèi)存失誤,dealloc需要重寫并對屬性置為nil

10.避免龐大的xib,storyBoard,盡量使用純代碼開發(fā)

CPU層面

1.Timer的時間間隔不宜太短,滿足需求即可
2.線程適量,不宜過多,不要阻塞主線程
3.優(yōu)化算法,減少循環(huán)次數(shù)
4.定位和藍牙按需取用,定位之后要關(guān)閉或降低定位頻率
5.一些硬件的使用,不使用就關(guān)掉

58.app 的包瘦身

image.png

59.講講 MVC火俄、MVVM、MVP讲冠,以及你在項目里具體是怎么寫的瓜客?

MVC  Model-view-controller 數(shù)據(jù)-視圖-控制器     
一般控制器用于管理數(shù)據(jù)和視圖, 數(shù)據(jù)和視圖交互都是通過控制器來進行的.視圖和數(shù)據(jù)進行了解耦, 但是我們?nèi)粘J褂媒?jīng)常會將模型綁定給視圖.模型封裝在視圖內(nèi)部,外部不用管理視圖內(nèi)部業(yè)務(wù)邏輯,這數(shù)據(jù) mvc 的變種, 控制器只給視圖模型數(shù)據(jù)就好了. 缺點是視圖和 模型有耦合;

MVVM Model-view-viewModel  模型-視圖-視圖模型
view 和 model 的交互通過viewmodel 來進行交互,實現(xiàn)數(shù)據(jù)的雙向綁定


MVP  Model-view - Presenter  模型-視圖-主持人

view 和 model 的交互通過Presenter,controller通過Presenter來管理 model 和 View

60.你自己用過哪些設(shè)計模式?

結(jié)合自己項目來講吧

61.一般開始做一個項目竿开,你的架構(gòu)是如何思考的谱仪?

根據(jù)模塊,使用 mvc 功能劃分..結(jié)合自己項目講比較容易
涉及到東西也比較多,比較雜,大到整個項目架構(gòu),小到一個 view 的架構(gòu);沒具體的答案
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市否彩,隨后出現(xiàn)的幾起案子疯攒,更是在濱河造成了極大的恐慌,老刑警劉巖列荔,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敬尺,死亡現(xiàn)場離奇詭異枚尼,居然都是意外死亡,警方通過查閱死者的電腦和手機砂吞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門署恍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蜻直,你說我怎么就攤上這事盯质。” “怎么了概而?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵呼巷,是天一觀的道長。 經(jīng)常有香客問我到腥,道長朵逝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任乡范,我火速辦了婚禮配名,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晋辆。我一直安慰自己渠脉,他們只是感情好,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布瓶佳。 她就那樣靜靜地躺著芋膘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霸饲。 梳的紋絲不亂的頭發(fā)上为朋,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音厚脉,去河邊找鬼习寸。 笑死,一個胖子當著我的面吹牛傻工,可吹牛的內(nèi)容都是我干的霞溪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼中捆,長吁一口氣:“原來是場噩夢啊……” “哼鸯匹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泄伪,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤殴蓬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后臂容,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體科雳,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡根蟹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了糟秘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片简逮。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尿赚,靈堂內(nèi)的尸體忽然破棺而出散庶,到底是詐尸還是另有隱情,我是刑警寧澤凌净,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布悲龟,位于F島的核電站,受9級特大地震影響冰寻,放射性物質(zhì)發(fā)生泄漏须教。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一斩芭、第九天 我趴在偏房一處隱蔽的房頂上張望轻腺。 院中可真熱鬧,春花似錦划乖、人聲如沸贬养。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽误算。三九已至,卻和暖如春迷殿,著一層夾襖步出監(jiān)牢的瞬間儿礼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工庆寺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜘犁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓止邮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奏窑。 傳聞我的和親對象是個殘疾皇子导披,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,101評論 1 32
  • 面向?qū)ο蟮娜筇匦裕悍庋b、繼承埃唯、多態(tài) OC內(nèi)存管理 _strong 引用計數(shù)器來控制對象的生命周期撩匕。 _weak...
    運氣不夠技術(shù)湊閱讀 1,100評論 0 10
  • 面向?qū)ο蟮娜筇卣?并作簡單的介紹。 面向?qū)ο蟮娜齻€基本特征是:封裝墨叛、繼承止毕、多態(tài)模蜡。 1.封裝是面向?qū)ο蟮奶卣髦?...
    xiny123閱讀 1,433評論 0 6
  • 1.設(shè)計模式是什么忍疾? 你知道哪些設(shè)計模式,并簡要敘述谨朝?設(shè)計模式是一種編碼經(jīng)驗卤妒,就是用比較成熟的邏輯去處理某一種類型...
    龍飝閱讀 2,151評論 0 12
  • 春節(jié)過后看了電影《無名之輩》覺得非常的感動,我們身邊每一個小人物字币,雖然看起來那么的卑微和渺小则披,可是他們身上依然...
    甘霖女俠講故事閱讀 594評論 1 6