前言
iOS中是怎么對內(nèi)存進行管理的?
iOS中有哪些多線程?
Block是什么?有哪些?
iOS中如何保存數(shù)據(jù)?有哪些持久化存儲機制?
什么是淺拷貝?什么是深拷貝?
1、iOS中是怎么對內(nèi)存進行管理的?
為什么要進行內(nèi)存管理?
??程序在運行的過程中,往往涉及到創(chuàng)建對象、定義變量、調用函數(shù)或方法,而這些行為都會增加程序的內(nèi)存占用。
??一個移動設備的內(nèi)存是有限的,每個軟件所能占用的內(nèi)存也是有限的。當程序所占用的內(nèi)存較多時,系統(tǒng)就會發(fā)出內(nèi)存警告,這時就得回收一些不需要再使用的內(nèi)存空間。比如不再使用的對象、變量等。
??如果程序占用內(nèi)存過大,系統(tǒng)可能會強制關閉程序,造成崩潰、閃退現(xiàn)象,影響用戶體驗。所以我們需要對內(nèi)存進行合理的分配、清除內(nèi)存、回收不再使用的對象,從而保證程序的穩(wěn)定性。
內(nèi)存組成是什么?
-
代碼區(qū):用于存放程序的代碼,即CPU執(zhí)行的機器指令,是只讀的。代碼區(qū)通常也位于代碼段。 -
常量區(qū):存儲常量數(shù)據(jù),如字符串常量。用于存儲已經(jīng)初始化的常量,這些數(shù)據(jù)在程序運行期間不可被修改,程序結束后由系統(tǒng)釋放。常量區(qū)通常位于代碼段。 -
全局(靜態(tài))區(qū):用于存儲全局變量和靜態(tài)變量,其生命周期與整個應用程序的運行周期相同。全局變量存儲在數(shù)據(jù)段,靜態(tài)變量存儲在BSS段(未初始化數(shù)據(jù)段)。 -
堆區(qū):是用于動態(tài)分配內(nèi)存的區(qū)域,用于存放進程運行中被動態(tài)分配的內(nèi)存段。在堆中分配的內(nèi)存由程序員負責管理,通常通過malloc、free或new、delete等操作。堆的大小通常受操作系統(tǒng)和硬件限制,且動態(tài)增長。 -
棧區(qū):用于存放程序臨時創(chuàng)建的變量、存放函數(shù)的參數(shù)值、局部變量等。由編譯器自動分配釋放。
怎么對內(nèi)存進行管理?
??在iOS中,內(nèi)存管理是一個重要而且必要的任務,以確保應用運行時不會出現(xiàn)內(nèi)存泄露或內(nèi)存過度使用問題。以下是一些iOS對內(nèi)存進行管理的關鍵概念和方法:
- 使用ARC的情況下
1、自動管理引用計數(shù):
在ARC環(huán)境下,引用計數(shù)是自動管理的。你無需手動調用retain、 release和autorelese。
// 示例:使用強引用
NSString *strongReference = [[NSString alloc] initWithFormat:@"Hello, World!"];
// 示例:使用弱引用
__weak NSString *weakReference = strongReference;
2、避免循環(huán)引用:
在Block中使用__weak來引用對象,以免循環(huán)引用(說明下,并非所有的Block都能產(chǎn)生循環(huán)引用)。
__weak typeof(self) weakSelf = self;
someBlock = ^{
[weakSelf doSomething];
};
- 在非ARC環(huán)境下
1、手動管理引用計數(shù):
在非ARC環(huán)境下,需要手動管理引用計數(shù),手動調用retain和release。
// 示例:手動管理引用計數(shù)
NSString *manualReference = [[NSString alloc] initWithFormat:@"Hello, World!"];
[manualReference retain]; // 增加引用計數(shù)
[manualReference release]; // 減少引用計數(shù)
2、使用自動釋放池:
在非ARC環(huán)境下,可以使用自動釋放池來延遲釋放對象
// 示例:使用自動釋放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *autoreleaseReference = [[[NSString alloc] initWithFormat:@"Hello, World!"] autorelease];
[pool drain]; // 手動釋放池中的對象
2、iOS中有哪些多線程?有什么區(qū)別?
??在iOS中,多線程是一種并發(fā)編程的技術,允許應用程序同時執(zhí)行多個任務。以下是iOS中常用的多線程技術,以及它們之間的一些區(qū)別:
-
NSThread:是Objective-C中的一個輕量級多線程類。你可以使用NSThread來創(chuàng)建和管理線程。但需要注意的是,NSThread的使用相對底層(提供了較為基礎的線程管理接口),需要手動管理線程的生命周期和線程執(zhí)行。
a、線程的創(chuàng)建和啟動
??需要手動創(chuàng)建線程對象、設置線程的執(zhí)行方法、啟動線程,并在適當?shù)臅r機手動管理線程的生命周期。
// 創(chuàng)建線程對象
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod) object:nil];
// 啟動線程
[myThread start];
b、線程同步和通信
??提供了基礎的線程同步和通信機制,例如使用performSelector:onThread:withObject:waitUntilDone:方法來在不同線程中執(zhí)行方法(UI更新(主線程上)、后臺任務通知主線程、線程之間的通信)。但相較于其他更高級的多線程技術,NSThread對線程同步較為有限。
1)、UI更新
[self performSelector:@selector(updateUI) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];
2)、后臺任務通知主線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 后臺任務完成
// 通知主線程執(zhí)行操作
[self performSelector:@selector(taskCompleted) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];
});
3)、線程之間的通信
// 在后臺線程中執(zhí)行任務
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(taskCompleted) onThread:myThread withObject:nil waitUntilDone:NO];
});
-
GCD:由蘋果提供的高級多線程技術,用于管理并發(fā)任務。GCD提供了一個簡單而強大的API,通過隊列來管理任務的執(zhí)行。GCD負責調度任務,開發(fā)者無需手動創(chuàng)建線程。GCD包括了串行隊列、并發(fā)隊列和全局隊列等概念。 -
NSOperation/NSOperationQueue:建立在GCD之上的更高級別的抽象。NSOperation定義了一個操作,而NSOperationQueue管理操作的執(zhí)行。這提供了更多的控制和額外的功能,例如操作之間的依賴關系、取消和暫停等。
區(qū)別:
-
抽象程度-
NSThread:較為底層,需要手動管理線程的創(chuàng)建、啟動和銷毀。 -
GCD:提供了更高級別的抽象,通過隊列管理任務。 -
NSOperationQueue:提供了更高級別的操作管理
-
-
手動與自動管理-
NSThread:手動管理線程的生命周期和執(zhí)行 -
GCD/NSOperationQueue:自動化的方式來管理并發(fā)任務,開發(fā)者只需關注任務的執(zhí)行邏輯。
-
-
隊列類型-
GCD/NSOperationQueue:包含了串行隊列和并發(fā)隊列,允許開發(fā)者輕松實現(xiàn)并發(fā)任務
-
-
依賴關系:串行隊列和并發(fā)隊列,同時支持設置最大并發(fā)數(shù)。-
GCD/NSOperationQueue:支持任務之間的依賴關系,可以指定一個任務在另一個任務完成后才執(zhí)行。使得任務之間的協(xié)作變得更為靈活。
-
3、Block是什么?有哪些?
??Block是一種在C語言基礎上構建的Objective-C特性,允許你創(chuàng)建一個封裝了一段代碼的對象,可以在程序中傳遞和執(zhí)行。Block也被稱為閉包,它捕獲了其定義時的環(huán)境,使得在執(zhí)行時可以訪問這個環(huán)境。
語法如下:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {
// block 體
};
1、無參數(shù)無返回值的Block
// 聲明并定義一個無參數(shù)無返回值的 Block
void (^simpleBlock)(void) = ^{
NSLog(@"This is a simple block.");
};
// 調用 Block
simpleBlock();
2、帶參數(shù)的Block
// 聲明并定義一個帶參數(shù)的 Block
void (^parameterBlock)(NSString *) = ^(NSString *name) {
NSLog(@"Hello, %@", name);
};
// 調用 Block
parameterBlock(@"John");
3、帶返回值的Block:
// 聲明并定義一個帶返回值的 Block
NSInteger (^addBlock)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
return a + b;
};
// 調用 Block
NSInteger result = addBlock(3, 5);
NSLog(@"Result: %ld", result);
4、Block作為方法參數(shù):
// 聲明一個方法,參數(shù)為 Block
- (void)performOperationWithBlock:(void (^)(void))operationBlock {
NSLog(@"Before Block");
// 執(zhí)行傳入的 Block
operationBlock();
NSLog(@"After Block");
}
// 調用方法,傳入 Block
[self performOperationWithBlock:^{
NSLog(@"Inside the Block");
}];
Block在異步線程、回調函數(shù)、動畫和集合操作等場景中被廣泛使用。
4、iOS中如何保存數(shù)據(jù)?有哪些持久化存儲機制?
1、UserDefaults:存儲小量數(shù)據(jù)的輕量級存儲方式,適用于簡單的配置信息、用戶偏好設置等。它基于鍵值對的方式進行數(shù)據(jù)存儲。
// 保存數(shù)據(jù)到UserDefaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"John" forKey:@"UserName"];
[userDefaults synchronize]; // 可選的,手動同步UserDefaults
// 從UserDefaults讀取數(shù)據(jù)
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *userName = [userDefaults objectForKey:@"UserName"];
2、文件存儲:可以使用NSData、NSString等進行數(shù)據(jù)的讀寫。
// 保存數(shù)據(jù)到文件
NSString *dataString = @"Hello, World!";
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"data.txt"];
[dataString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
// 從文件中讀取數(shù)據(jù)
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"data.txt"];
NSString *dataString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
3、Core Data:一個面向對象的數(shù)據(jù)持久化框架,適用于處理大量結構化的數(shù)據(jù)。提供了對象關系映射和查詢功能。
// Core Data 的基本使用
NSManagedObjectContext *context = // 獲取上下文對象
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:@"EntityName" inManagedObjectContext:context];
[newObject setValue:@"John" forKey:@"name"];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Save failed: %@", [error localizedDescription]);
}
4、SQLite數(shù)據(jù)庫:可以直接使用SQLite API
或者使用封裝庫(FMDB)來實現(xiàn)
// 使用 FMDB 操作 SQLite 數(shù)據(jù)庫
FMDatabase *db = [FMDatabase databaseWithPath:databasePath];
if (![db open]) {
NSLog(@"Could not open database.");
return;
}
NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO TableName (name, age) VALUES ('%@', %ld)", name, age];
[db executeUpdate:insertSQL];
[db close];
??這些是iOS中常用的一些持久化存儲機制,選擇適當?shù)姆绞饺Q于應用程序的需求和數(shù)據(jù)的性質。
-
UserDefaults適用于小量數(shù)據(jù); -
文件存儲適用于簡單文本或二進制數(shù)據(jù); -
Core Data適用于復雜的數(shù)據(jù)模型; -
SQLite則適用于需要關系型數(shù)據(jù)庫的情況。
5、什么是淺拷貝?什么是深拷貝?
??淺拷貝和深拷貝是在編程中經(jīng)常遇到的兩個概念,它們描述了在復制對象或數(shù)據(jù)結構時所采用的不同策略。
-
淺拷貝:創(chuàng)建一個新的對象,但不復制對象中的所有內(nèi)容。只復制對象的引用,而不復制引用指向的內(nèi)容。因此,原始對象和淺拷貝后的對象共享相同的子對象。如果修改了原始對象中的共享子對象,淺拷貝后的對象也會受到影響。
#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSMutableArray *hobbies;
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone {
Person *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
copy.hobbies = [self.hobbies mutableCopy];
return copy;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person1 = [[Person alloc] init];
person1.name = @"John";
person1.hobbies = [NSMutableArray arrayWithObjects:@"Reading", @"Swimming", nil];
// 淺拷貝
Person *person2 = [person1 copy];
// 修改原始對象的屬性
[person1.hobbies addObject:@"Traveling"];
// 淺拷貝后的對象也受到影響
NSLog(@"Person 1 Hobbies: %@", person1.hobbies); // 輸出: Reading, Swimming, Traveling
NSLog(@"Person 2 Hobbies: %@", person2.hobbies); // 輸出: Reading, Swimming, Traveling
}
return 0;
}
??Person類實現(xiàn)了NSCopying協(xié)議,并重寫了copyWithZone:方法進行淺拷貝。通過copy方法創(chuàng)建的person2對象和person1對象共享相同的hobbies數(shù)組,因此修改一個對象的hobbies
數(shù)組會影響到另一個對象。
-
深拷貝:創(chuàng)建一個新的對象,并遞歸地復制對象中的所有內(nèi)容,包括對象所引用的其他對象。深拷貝生成的新對象與原始對象完全獨立,對一個對象的修改不會影響影響到另一個對象。
#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSMutableArray *hobbies;
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone {
Person *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
copy.hobbies = [[NSMutableArray alloc] initWithArray:self.hobbies copyItems:YES];
return copy;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person1 = [[Person alloc] init];
person1.name = @"John";
person1.hobbies = [NSMutableArray arrayWithObjects:@"Reading", @"Swimming", nil];
// 深拷貝
Person *person2 = [person1 copy];
// 修改原始對象的屬性
[person1.hobbies addObject:@"Traveling"];
// 深拷貝后的對象不受影響
NSLog(@"Person 1 Hobbies: %@", person1.hobbies); // 輸出: Reading, Swimming, Traveling
NSLog(@"Person 2 Hobbies: %@", person2.hobbies); // 輸出: Reading, Swimming
}
return 0;
}
??Person類的copyWithZone:方法中使用了initWithArray:copyItems:方法,這會遞歸地復制數(shù)組中的每個元素,從而創(chuàng)建了一個全新的hobbies數(shù)組,使得修改一個對象的hobbies不會影響到另一個對象。
讓我們通過一個生活中的比喻來理解:
??假設你制作了一個購物清單,上面列有你需要購買的物品。現(xiàn)在你和你朋友決定共享這個購物清單,以便兩人都知道需要購買什么。
淺拷貝:你和朋友共用這一份購物清單。如果你修改了清單上的某個物品,這個變化也會影響到你朋友,因為你們就這一份,按著上面的來購物。
深拷貝:你制作了一份購物清單的副本,你們兩個各自拿著一張清單。這個時候,你修改了自己的清單,朋友那是不受到影響的。