前言
6、什么是KVC、KVO?
7、通知和協(xié)議的不同之處?
8、單例是什么?優(yōu)缺點是什么?
9、什么是懶加載?優(yōu)缺點是什么?
10、static什么時候使用?有什么作用?
6、什么是KVC、KVO?
KVC(Key-Value Coding)
??KVC是一種通過鍵值訪問對象的屬性的機(jī)制,允許通過鍵(屬性名)來訪問和修改對象的屬性值。這使得我們可以通過字符串形式訪問對象的屬性,而不需要直接調(diào)用相應(yīng)的方法。
在項目中,以下一些情況下你可能會使用KVC:
1、設(shè)置和獲取對象的屬性
??KVC提供了一種方便的方式來設(shè)置和獲取對象的屬性,尤其是當(dāng)屬性名以字符串形式存在時。這對于在運(yùn)行時動態(tài)地訪問或修改對象的屬性非常有用。
// 設(shè)置對象屬性
[object setValue:@"John" forKey:@"name"];
// 獲取對象屬性
NSString *name = [object valueForKey:@"name"];
2、字典和模型的轉(zhuǎn)換
??當(dāng)從服務(wù)器或其他數(shù)據(jù)源獲得數(shù)據(jù)時,通常以字典的形式提供。使用KVC,你可以方便將字典中的鍵值對映射到對象的屬性,實現(xiàn)字典和模型對象之間的轉(zhuǎn)換。
NSDictionary *data = @{@"name": @"John", @"age": @25};
// 使用KVC將字典轉(zhuǎn)換為對象
Person *person = [[Person alloc] init];
[person setValuesForKeysWithDictionary:data];
3、動態(tài)地訪問屬性
??當(dāng)需要動態(tài)地訪問或操作對象的屬性時,KVC提供了一種方便的機(jī)制。這對于通用的數(shù)據(jù)操作、表單處理等場景非常有用。
NSString *propertyName = @"name";
NSString *propertyValue = [object valueForKey:propertyName];
4、集合操作
??KVC提供了一套用于集合操作的方法,例如對數(shù)組中的每個元素執(zhí)行某個操作、計算數(shù)組中的最大或最小值等。這對于處理集合數(shù)據(jù)非常方便。
NSArray *numbers = @[ @10, @20, @30 ];
// 計算數(shù)組中的總和
NSNumber *sum = [numbers valueForKeyPath:@"@sum.self"];
5、在自定義控制臺輸出中使用
??在調(diào)試時,通過覆蓋description方法并使用KVC來生成自定義控制臺輸出,可以方便地查看對象的內(nèi)部狀態(tài)。
- (NSString *)description {
return [NSString stringWithFormat:@"Person - Name: %@, Age: %@", [self valueForKey:@"name"], [self valueForKey:@"age"]];
}
優(yōu)點:
-
靈活性:提供了一種靈活的方式來訪問和修改對象的屬性,特別是在處理動態(tài)和未知屬性的情況下。 -
簡化代碼:通過KVC,可以減少訪問和修改對象屬性的代碼量,使代碼更加簡潔。
//正常情況下某個類的創(chuàng)建
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
//使用KVC
NSDictionary *data = @{@"name": @"John", @"age": @25};
// 使用 KVC 將字典轉(zhuǎn)換為對象
Person *person = [[Person alloc] init];
[person setValuesForKeysWithDictionary:data];
-
便于集合操作:提供了一套用于集合操作的方法,例如對數(shù)組中的每個元素執(zhí)行某個操作、計算數(shù)組中的最大或最小值等。
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
// 創(chuàng)建包含 Person 對象的數(shù)組
NSArray *people = @[ [[Person alloc] initWithName:@"John"],
[[Person alloc] initWithName:@"Jane"],
[[Person alloc] initWithName:@"Bob"] ];
// 使用 KVC 獲取所有人的姓名組成的新數(shù)組
NSArray *names = [people valueForKeyPath:@"@each.name"];
NSLog(@"Names: %@", names);
-
實現(xiàn)通用方法:使得能夠?qū)崿F(xiàn)通用的數(shù)據(jù) 操作,例如字典和模型之間的轉(zhuǎn)換。
缺點
-
運(yùn)行時錯誤:由于KVC使用字符串形式來訪問屬性,編譯器無法提供錯誤檢查,因此在運(yùn)行時可能會發(fā)生拼寫錯誤或傳遞了不存在的屬性名,導(dǎo)致崩潰。 -
性能開銷:相對于直接方法調(diào)用,KVC的性能開銷較大。因此,對性能有嚴(yán)格要求的場景下,可能不適合大規(guī)模使用KVC。 -
不支持編譯器優(yōu)化:不容易被編譯器優(yōu)化,因為它是在運(yùn)行時動態(tài)解析的,這可能導(dǎo)致一些性能上的損失。
KVO(Key-Value Observing)
??KVO允許一個對象監(jiān)聽另一個對象特定屬性值的變化。當(dāng)被觀察的對象的屬性發(fā)生改變時,觀察者會收到通知。這提供了一種在對象之間保持同步的機(jī)制。
在項目中,以下一些情況下你可能會使用KVO:
1、UI更新
??當(dāng)數(shù)據(jù)模型的屬性變化時,需要更新用戶界面。
// 添加 KVO 觀察者
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
// 實現(xiàn) KVO 觀察者方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
// 在這里執(zhí)行刷新 UI 的操作
[self updateUIWithNewName:change[NSKeyValueChangeNewKey]];
}
}
2、數(shù)據(jù)同步
??當(dāng)多個對象之間需要保持?jǐn)?shù)據(jù)同步時,可以使用KVO。一個對象的屬性變化將會通知到觀察者,觀察者可以做出相應(yīng)的響應(yīng),保持?jǐn)?shù)據(jù)的一致性。
3、觀察屬性變化執(zhí)行特定操作
??在屬性變化時,需要執(zhí)行一些特點的操作,而不是僅僅更新UI。
4、監(jiān)控網(wǎng)絡(luò)請求狀態(tài)
??在進(jìn)行網(wǎng)絡(luò)請求時,有時需要監(jiān)控網(wǎng)絡(luò)請求的狀態(tài)變化,以便在請求完成時執(zhí)行一些操作。
5、自定義觀察者模式
??KVO提供了一種機(jī)制來實現(xiàn)觀察者模式,用于建立對象之間的通信。這樣可以減少對象之間的直接耦合。
ps:使用KVO時應(yīng)該遵循一些最佳實踐,如正確地注冊和移除觀察者,處理觀察者通知時的線程安全等。
優(yōu)點
-
松耦合:KVO實現(xiàn)了觀察者模式,使得觀察者(監(jiān)聽者)和被觀察者之間的耦合度較低。對象不需要直接調(diào)用觀察者的方法,而是通過通知的方式進(jìn)行通信。 -
動態(tài)性:KVO允許在運(yùn)行時動態(tài)地注冊和取消觀察者,以及動態(tài)地選擇觀察的屬性。這種動態(tài)性使得在不同場景下適應(yīng)變化更加容易。
// 被觀察的對象
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
// 觀察者
@interface Observer : NSObject
@end
@implementation Observer
- (instancetype)init {
self = [super init];
if (self) {
// 創(chuàng)建被觀察對象
Person *person = [[Person alloc] init];
// 添加觀察者
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
// 模擬屬性變化,觸發(fā) KVO
person.name = @"John";
// 移除觀察者
[person removeObserver:self forKeyPath:@"name"];
}
return self;
}
// 實現(xiàn) KVO 觀察者方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"Name changed to: %@", change[NSKeyValueChangeNewKey]);
}
}
@end
-
簡化代碼:使用KVO可以簡化代碼,特別是在需要響應(yīng)屬性變化并執(zhí)行相應(yīng)操作的情況下。這有助于避免在代碼中插入大量的回調(diào)方法。
缺點
-
難以調(diào)試:由于KVO是在運(yùn)行時進(jìn)行動態(tài)注冊和移除的,因此在調(diào)試時可能會導(dǎo)致一些難以追蹤的問題。觀察者的添加和移除時機(jī)需要特別注意,否則可能會導(dǎo)致崩潰或不符合預(yù)期的行為。 -
性能開銷:KVO的實現(xiàn)涉及到動態(tài)方法解析和消息轉(zhuǎn)發(fā),相對于直接調(diào)用方法來說,會有一些性能開銷。在性能要求較高的場景中,可能需要謹(jǐn)慎使用KVO。 -
不支持對基本數(shù)據(jù)類型的直接觀察:KVO主要適用于對象,不直接支持對基本數(shù)據(jù)類型(例如NSInteger、CGFloat等)的觀察。
7、通知和協(xié)議的不同之處?
??通知和協(xié)議時iOS中兩種不同的機(jī)制,用于實現(xiàn)對象之間的通信和協(xié)作。
通知
-
發(fā)布/訂閱模式:采用發(fā)布/訂閱模式,允許一個對象發(fā)布通知,而其他對象則可以注冊成為觀察者并在通知發(fā)生時得到通知。 -
中心化管理:通知的管理由通知中心(NSNotificationCenter)來完成,對象通過通知中心注冊和接收通知。 -
一對多關(guān)系:一個通知可以有多個觀察者,觀察者可以訂閱多個通知。 -
松散耦合:發(fā)送通知的對象和接收通知的對象之間的關(guān)系相對松散,它們不需要直接知道對方的存在。 -
異步:通知是異步的,通知的發(fā)送和接收不需要在同一時間和線程上。
// 發(fā)送通知的代碼
[[NSNotificationCenter defaultCenter] postNotificationName:@"SomeNotification" object:nil];
// 接收通知的代碼
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"SomeNotification" object:nil];
// 通知處理方法
- (void)handleNotification:(NSNotification *)notification {
NSLog(@"Received notification: %@", notification.name);
}
協(xié)議
-
定義接口:協(xié)議定義了一組方法,表示一種接口,對象通過采用協(xié)議來表明它們實現(xiàn)了這個接口。 -
實現(xiàn):一個類可以采用一個或多個協(xié)議,表示它們具有協(xié)議定義的方法。 -
強(qiáng)制執(zhí)行:采用協(xié)議的類必須實現(xiàn)協(xié)議中定義的所有方法,否則會在編譯時報錯。 -
一對一關(guān)系:協(xié)議通常用于實現(xiàn)一對一的關(guān)系,一個類實現(xiàn)一個協(xié)議。 -
編譯時檢查:編譯器會檢查是否實現(xiàn)了協(xié)議中的所有方法,確保符合協(xié)議的要求
// 定義一個協(xié)議
@protocol MyProtocol
- (void)doSomething;
@end
// 類采用協(xié)議
@interface MyClass : NSObject <MyProtocol>
@end
@implementation MyClass
- (void)doSomething {
NSLog(@"Doing something");
}
@end
如果通信涉及到多個對象,且這些對象之間的關(guān)系比較松散,通知是一個不錯的選擇。
如果通信是一對一的關(guān)系,并且需要確保采用協(xié)議的對象實現(xiàn)了一組特定的方法,協(xié)議是更適合的選擇。
8、單例是什么?優(yōu)缺點是什么?
??單例是一種設(shè)計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取該實例。單例模式通常使用靜態(tài)方法或類方法(類似于sharedInstance)來返回唯一的實例。
優(yōu)點
-
全局訪問:可以在整個應(yīng)用程序中通過單一的訪問點獲取相同的實例,方便數(shù)據(jù)共享和操作。 -
避免重復(fù)創(chuàng)建:由于單例只有一個實例,可以避免重復(fù)創(chuàng)建相同類型的對象,減少資源占用。 -
共享狀態(tài):單例可以用于共享狀態(tài),例如應(yīng)用配置信息、登錄狀態(tài)等。
缺點
-
全局狀態(tài):單例引入了全局狀態(tài),可能會導(dǎo)致代碼的耦合性增加,使得代碼難以測試和維護(hù)。 -
隱藏依賴:在某些情況下,單例可能會隱藏類之間的依賴關(guān)系,增加代碼的復(fù)雜性。 -
生命周期管理:單例的生命周期通常貫穿整個應(yīng)用程序,可能會導(dǎo)致對象持有時間過長,無法及時釋放。
// MySingleton.h
@interface MySingleton : NSObject
@property (nonatomic, strong) NSString *data;
+ (instancetype)sharedInstance;
@end
// MySingleton.m
@implementation MySingleton
// 靜態(tài)變量用于保存唯一實例
static MySingleton *_sharedInstance = nil;
+ (instancetype)sharedInstance {
// 使用GCD確保線程安全創(chuàng)建單例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[super alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
// 防止通過init方法創(chuàng)建新的實例
NSAssert(!_sharedInstance, @"Use sharedInstance method to get the instance.");
self = [super init];
if (self) {
// 初始化
}
return self;
}
@end
9、什么是懶加載?優(yōu)缺點是什么?
??懶加載是一種延遲加載對象或執(zhí)行操作的技術(shù),它在需要的時候才進(jìn)行加載或執(zhí)行,而不是在應(yīng)用啟動時就立即加載或執(zhí)行。懶加載通常用于延遲加載視圖、數(shù)據(jù)或其他資源,以提高應(yīng)用程序的性能和資源利用率。
優(yōu)點
性能優(yōu)化:懶加載可以減少應(yīng)用程序啟動時的資源消耗,因為只有在需要的時候才會加載相應(yīng)的對象或執(zhí)行相關(guān)的操作。這有助于提高應(yīng)用程序的啟動速度和響應(yīng)性能。節(jié)省資源:對于一些不一定會被使用的對象或數(shù)據(jù),懶加載可以避免不必要的資源占用,從而節(jié)省內(nèi)存和其他系統(tǒng)資源。更快的啟動時間:通過延遲加載,應(yīng)用程序啟動時需要加載的資源較少,從而加快啟動時間。
缺點
復(fù)雜性增加:在代碼中使用懶加載可能會增加代碼的復(fù)雜性,因為你需要管理對象的狀態(tài)并確保在需要時正確地進(jìn)行加載。這可能導(dǎo)致一些潛在的錯誤,如內(nèi)存泄露或邏輯錯誤。可能引起延遲:如果懶加載的時機(jī)選擇不當(dāng),可能導(dǎo)致在需要對象或數(shù)據(jù)時出現(xiàn)短暫的延遲,因為此時才進(jìn)行加載。可能影響代碼可讀性:過度使用懶加載可能會導(dǎo)致代碼難以理解,特別是對于新加入的開發(fā)人員。適度使用,并在必要時添加注釋以解釋懶加載的原因和時機(jī)。
10、static什么時候使用?有什么作用?
??static關(guān)鍵字用于不同的上下文,其作用取決于它所修飾的內(nèi)容。
1、靜態(tài)局部變量:在方法調(diào)用之間保留其值,而不像普通局部變量那樣在每次調(diào)用時重新初始化。這可以用于實現(xiàn)在方法之間保持狀態(tài)的需求。
- (void)someMethod {
static NSInteger counter = 0;
counter++;
NSLog(@"Counter: %ld", counter);
}
2、靜態(tài)全局變量:在文件范圍內(nèi)可見,但是其作用域限制在當(dāng)前文件中。這可以用于在整個文件中共享狀態(tài)或數(shù)據(jù)。
// 在文件的頂部或?qū)崿F(xiàn)文件的全局作用域內(nèi)
static NSInteger globalCounter = 0;
- (void)someMethod {
globalCounter++;
NSLog(@"Global Counter: %ld", globalCounter);
}
3、靜態(tài)函數(shù):作用域限制在當(dāng)前文件中,不可被其他文件訪問。這可以用于實現(xiàn)文件內(nèi)部的私有函數(shù),不暴露給其他文件使用。
// 在文件的頂部或?qū)崿F(xiàn)文件的全局作用域內(nèi)
static NSInteger multiplyByTwo(NSInteger number) {
return number * 2;
}
- (void)someMethod {
NSInteger result = multiplyByTwo(5);
NSLog(@"Result: %ld", result);
}
4、靜態(tài)常量:用于定義在整個文件內(nèi)都可見的常量,避免魔法數(shù)字的使用。常量通常以static const的形式定義,確保其在編譯時被優(yōu)化。
static const NSInteger MaxItemCount = 10;
- (void)someMethod {
NSLog(@"Max Item Count: %ld", MaxItemCount);
}
static的主要作用是限制變量或函數(shù)的作用域,使其僅在定義它們的文件內(nèi)可見。在
方法內(nèi)部使用static關(guān)鍵字可以使局部變量在方法調(diào)用之間保留其值。在
全局范圍內(nèi)使用static可以限制變量、函數(shù)或常量的作用域,使其在當(dāng)前文件中可見。