守望寒冬,多喝燙水。
-
NSObject物件建立例項物件的時候系統分配了16個記憶體(通過malloc_size函式可獲得)
-
但是 NSObject只使用了8個位元組 使用(class_getinstanceSize可獲得)
3. 物件的isa指標指向哪裡?
- instance物件的isa指標指向class物件
- class物件的isa指標指向 meta-class物件
- meta-class物件的isa指標基類的meta-class物件
4. OC類的資訊儲存在哪裡?
- meta-class儲存:類方法
- class物件儲存:物件方法,屬性,成員變數,協議資訊
- instance儲存:成員變數具體的值
5. 說說你對函式呼叫的理解吧。
- 函式呼叫 實際實際上就是 給物件傳送一條訊息
- objc_messageSend(物件, @selectir(物件方法))
- 尋找順序(物件方法)instance的isa指標找到類物件 在類物件中尋找方法,若沒有向superClass中查詢。
- 尋找順序(類方法)instance的isa指標找到類物件 --> 類物件的isa找到meta-calss --> 在meta-class物件中尋找類方法,若沒有向superClass中查詢。
KVO
1. iOS用什麼方式實現對一個物件的KVO?(KVO的本質是什麼?)
- 利用Runtime API 動態生成了一個新的類, 並且instance物件的isa指標指向這個生成的新子類。
-
當修改instance物件的屬性時,會呼叫Foundation的_NSSetXXXValueForKey函式
- willChangeValueForKey
- 父類原來的set方法
- didChangeValueForKey
- didChangeValueForKey 內部會觸發observerValueForKeyPath方法實現監聽。
KVC
1. 使用KVC會不會呼叫KVO?
-
會呼叫KVO, 因為他的內部使用了:
- willChangeValueForKey
- 直接去_方式去更改
- didChangeValueForKey
- didChangeValueForKey 內部會觸發
2. KVC的賦值和取值過程是怎麼樣的?原理是什麼?
-
賦值
-
setKey、_setKey 順序查詢方法
- 有: 傳遞引數呼叫防範
- 沒有: 檢視accessInstanceVariablesDirectly 方法返回值yes 繼續查詢
- _key、_iskey、key、iskey 順序查詢
-
setKey、_setKey 順序查詢方法
Category:
1. Category的使用場合是什麼?
2. Category中的屬性是否也存在類物件中?如果存在是怎麼生成和存在的?如果不存在,它存在的位置在哪裡?
- 一個類永遠只有一個類物件
- 在執行起來之後 最重都會合並在 類物件中去。
3. Category的使用原理是什麼?實現過程
-
Category在編譯的時候會將 方法、屬性、協議的資料合併到一個大陣列中,
- 後參加編譯一的資料會放在陣列的前面
- 在執行時的時候,runtime會將Category的資料合併到類的資訊中(類物件、元類物件中)
- 分類的呼叫順序
4. Category和Extension的區別是什麼?
- Category 在執行的時候才將資料合併到類資訊中
- Extension 在編譯的時候就將資料包含在類資訊中了 @interface Xxxx() 也叫做匿名分類
5. Category中有load方法嗎?load是什麼時候呼叫的?load方法能繼承碼?
- 有load方法
- load方法在runtime載入類、分類的時候呼叫
- load方法可以繼承,但是一般情況下不會主動去呼叫load方法,都是讓系統自動呼叫
6. load、initialize方法的區別是什麼?它們在Category中的呼叫順序?以及出現繼承時他們之間的呼叫過程?
-
呼叫方式:
- load 根據函式地址直接呼叫
- initialize 是通過objc_msgSend呼叫
-
呼叫時刻:
- load是runtime載入類、分類的時候呼叫(只會呼叫1次)
- initialize 是類第一次接收到訊息的時候呼叫、每一個類只會呼叫1次(但是父類的initialize方法可能會呼叫多次) 有些子類沒有initialize方法所以呼叫父類的。
-
呼叫順序:
-
load:
-
先呼叫類的load
- 先編譯的類,優先呼叫load
- 呼叫子類的load之前會先呼叫父類的load
-
在呼叫分類的load
- 先編譯的分類,優先呼叫load
-
先呼叫類的load
-
initialize
- 初始化父類
- 在初始化子類(可能最終呼叫的是父類的initialize方法)
-
load:
7. Category能否新增成員變數?如果可以,如何給Category新增成員變數?
-
不能直接給Category新增成員變數,但是可以間接新增。
- 使用一個全域性的字典 (缺點: 每一個屬相都需要一套相同的程式碼)
///> DLPerson+Test.h @interface DLPerson (Test) ///> 如果直接使用 @property 只會生成方法的聲名 不會生成成員變數和set、get方法的實現。 @property (nonatomic, assign) int weigjt; @end ///> DLPerson+Test.m #import "DLPerson+Test.h" @implemention DLPerson (Test) NSMutableDictionary weights_; + (void)load{ weights_ = [NSMutableDictionary alloc]init]; } - (void)setWeight:(int)weight{ NSString *key = [NSString stringWithFormat:@"%p",self]; weights_[key] = @(weight); } - (int)weight{ NSString *key = [NSString stringWithFormat:@"%p",self]; return [weights_[key] intValue] } @end 複製程式碼
-
使用runtime機制給分類新增屬性
#import<objc/runtime.h> const void *DLNameKey = &DLNameKey ///> 新增關聯物件 void objc_setAssociatedObject( id object,///>給哪一個物件新增關聯物件 const void * key,///>指標(賦值取值的key)&DLNameKey id value,///>關聯的值 objc_AssociationPolicy policy ///>關聯策略 下方表格 ) eg : objc_setAssociatedObject(self,@selector(name),name,OBJC_ASSOCIATION_COPY_NONATOMIC); ///> 獲得關聯物件 id objc_getAssociatedObject( id object,///>哪一個物件的關聯物件 const void * key///>指標(賦值取值的key) ) eg: objc_getAssociatedObject(self,@selector(name)); /// _cmd== @selector(name); objc_getAssociatedObject(self,_cmd); ///> 移除所有的關聯物件 void objc_removeAssociatedObjects( id object///> ) 複製程式碼
- objc_AssociationPolicy(關聯策略)
objc_AssociationPolicy(關聯策略) 對應的修飾符 OBJC_ASSOCIATION_ASSIGN assign OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic OBJC_ASSOCIATION_RETAIN strong, atomic OBJC_ASSOCIATION_COPY copy, atomic
Block
1. block的原理是怎樣的?本質是什麼
- block的本質就是一個oc物件內部也有isa指標, 封裝了函式及呼叫環境的OC物件,
2. 看程式碼解釋原因
int main(int argc, const char *argv[]){ @autoreleasepool{ int age = 10; void(^block)(void) = ^{ NSLog(@" age is %d ",age); }; age = 20; block(); } } /* 輸出結果為? 為什麼? 輸出結果是: 10 如果沒有修飾符預設是auto 為了能訪問外部的變數 block有一個變數捕獲的機制 因為他是區域性變數 並且沒有用static修飾 所以它被捕獲到block中是 一個值,外部再次改變時 block中的age不會改變。 */ 複製程式碼
變數型別 | 捕獲到Block內部 | 訪問方式 | |
---|---|---|---|
區域性變數 | auto | :white_check_mark: | 值傳遞 |
區域性變數 | static | :white_check_mark: | 指標傳遞 |
全域性變數 | :x: | 直接訪問 |
int main(int argc, const char *argv[]){ @autoreleasepool{ int age = 10; static int height = 10; void(^block)(void) = ^{ NSLog(@" age is %d, height is %d",age, height); }; age = 20; height = 20; block(); } } /* 輸出結果為? 為什麼? age is 10, height is 20 區域性變數用static 修飾之後 捕獲到block中的是 height的指標, 因此修改通過指標修改變數之後 外部的變數也被修改了 */ 複製程式碼
int age = 10; static int height = 10; int main(int argc, const char *argv[]){ @autoreleasepool{ void(^block)(void) = ^{ NSLog(@" age is %d, height is %d",age, height); }; age = 20; height = 20; block(); } } /* 輸出結果為? 為什麼? age is 20, height is 20 因為 age 和 height是全域性變數不需要捕獲直接就可以修改 全域性變數 對應該就可以訪問, 區域性變數 需要跨函式訪問,所以需要捕獲 因此修改通過指標修改變數之後 外部的變數也被修改了 */ 複製程式碼
int main(int argc, const char *argv[]){ @autoreleasepool{ void(^block)(void) = ^{ NSLog(@" self %p",self); }; block(); } } /* self 會不會被捕獲? 因為函式預設會有兩個引數 void test(DLPerson *self, SEL _cmd) 所以self 也是一個區域性變數 訪問@property(nonatmic, copy) NSString *name; 因為他是成員變數, 訪問的時候 用self.name 或者 self->_name 訪問所以 block在內部會捕獲self。 */ 複製程式碼
3. 既然block是一個OC物件,那麼block的物件型別是什麼?
-
ios記憶體分為5發區域
- 堆: 動態分配記憶體,需要程式設計師申請,也需要程式設計師自己管理(alloc、malloc等...)
- 棧: 放一些區域性變數,臨時變數 系統自己管理
- 靜態區(全域性區): 存放全域性的靜態物件。(編譯時分配,APP結束由系統釋放)
- 常量區: 常量。(編譯時分配,APP結束由系統釋放)
- 程式碼區: 程式區
-
block有三種類型,最終都繼承自NSBlock型別
- superClassNSGlobalBlock : __NSGlobalBlock : NSBlock : NSObject
block型別 環境 存放位置 NSGlobalBlock 沒有訪問auto變數 靜態區 NSStackBlock 訪問了auto變數 棧 NSMallocBlock __NSStackBlock__呼叫了copy 堆 -
__NSStackBlock__呼叫了copy 程式碼例項
void (^block)(void); void (^block1)(void); void test2(){ int age = 10; block = ^{ NSLog("age is %d",age); /* 因為 block訪問了 auto變數 所以目前block的型別為NSStackBlock型別, 存放的位置在棧上 在 main訪問是這個block已經被釋放了。 */ }; [block1 = ^{ NSLog("age is %d",age); /* 因為 block訪問了 auto變數 並且 進行了copy操作 所以目前block的型別為 NSMallocBlock 型別 存放的位置在堆上 在 main訪問是這個block是可以被訪問的。 */ } copy]; } int main(int argc, const char *argv[]){ @autoreleasepool{ test2(); block(); /// 輸出的值為 很大不是想要的值 block1();/// 輸出的值是10 } } 複製程式碼
-
每一種型別的block呼叫了copy之後結果如下所示
block的型別 副本源的配置儲存域 複製後的區域 NSGlobalBlock 棧 從棧複製到堆 NSStackBlock 程式的資料區域 不做 NSMallocBlock 堆 引用計數器+1
4. 在什麼情況下 ARC環境下,編譯器會根據情況自動將棧上的block複製到堆上?
-
block作為函式的返回值的時候
-
將block賦值給__strong指標時
-
block作為 Cocoa API中方法名含有usingBlock的方法引數時
NSArray *arr = @[]; /// 遍歷陣列中包含usingBlock方法的引數 [arr enumerateObjectUsingBlock:^(id _Nonnullobj, NSUInteger idx, Bool _Nonnull stop){ }] ; 複製程式碼
-
block作為GCD屬性的建議寫法
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); disPatch_after(disPatch_time(IDSPATCH_TIME_NOW, (int64_t)(delayInSecounds *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ }); 複製程式碼
-
MRC下block屬性建議寫法
- @property (copy, nonatomic) void (^block)(void);
-
ARM下block屬性建議寫法
- @property (strong, nonatomic) void (^block)(void);
- @property (copy, nonatomic) void (^block)(void);
5. __weak的作用是什麼?有什麼使用注意點?
- __weak 是一個修飾符
-
當block內部訪問的物件型別的auto變數
- 如果block是在棧上,將不會對auto變數產生強引用
-
如果Block被拷貝到堆上
- 會呼叫block內部的copy函式
- copy函式會呼叫原始碼中的_Block_object_assign函式
- _Block_object_assign函式會根據修飾 auto 變數的修飾符(__strong、__weak 、__unsafe_unretained)來決定作出相應的操作,形成強引用或者弱引用
-
block從對上移除
- 會呼叫block內部的dispose函式
- dispose函式會呼叫原始碼中的 _Block_object_dispose函式
- _Block_object_dispose函式會自動釋放auto變數(release)
6. __block的作用是什麼?有什麼使用注意點?
- __block修飾之後會將變數包裝成一個物件 可以解決block內部無法修改auto變數的問題
- 包裝秤物件之後就可以通過指標修改 外部的變量了