iOS基础 2.0

前言

iOS中是怎么对内存进行管理的?
iOS中有哪些多线程?
Block是什么?有哪些?
iOS中如何保存数据?有哪些持久化存储机制?
什么是浅拷贝?什么是深拷贝?

1、iOS中是怎么对内存进行管理的?

为什么要进行内存管理

  程序在运行的过程中,往往涉及到创建对象定义变量调用函数或方法,而这些行为都会增加程序的内存占用。
  一个移动设备的内存是有限的,每个软件所能占用的内存也是有限的。当程序所占用的内存较多时,系统就会发出内存警告,这时就得回收一些不需要再使用的内存空间。比如不再使用的对象变量等。
  如果程序占用内存过大,系统可能会强制关闭程序,造成崩溃闪退现象,影响用户体验。所以我们需要对内存进行合理的分配清除内存回收不再使用的对象,从而保证程序的稳定性

内存组成是什么

  • 代码区:用于存放程序的代码,即CPU执行的机器指令,是只读的。代码区通常也位于代码段
  • 常量区:存储常量数据,如字符串常量。用于存储已经初始化的常量,这些数据在程序运行期间不可被修改,程序结束后由系统释放。常量区通常位于代码段
  • 全局(静态)区:用于存储全局变量静态变量,其生命周期与整个应用程序的运行周期相同全局变量存储在数据段静态变量存储在BSS段(未初始化数据段)。
  • 堆区:是用于动态分配内存的区域,用于存放进程运行中被动态分配的内存段。在堆中分配的内存由程序员负责管理,通常通过mallocfreenewdelete等操作。堆的大小通常受操作系统硬件限制,且动态增长
  • 栈区:用于存放程序临时创建的变量存放函数的参数值局部变量等。由编译器自动分配释放。

怎么对内存进行管理

  在iOS中,内存管理是一个重要而且必要的任务,以确保应用运行时不会出现内存泄露内存过度使用问题。以下是一些iOS对内存进行管理的关键概念和方法:

  • 使用ARC的情况下

1、自动管理引用计数:
在ARC环境下,引用计数是自动管理的。你无需手动调用retainreleaseautorelese

// 示例:使用强引用
NSString *strongReference = [[NSString alloc] initWithFormat:@"Hello, World!"];

// 示例:使用弱引用
__weak NSString *weakReference = strongReference;

2、避免循环引用:
在Block中使用__weak来引用对象,以免循环引用(说明下,并非所有的Block都能产生循环引用)。

__weak typeof(self) weakSelf = self;
someBlock = ^{
    [weakSelf doSomething];
};
  • 在非ARC环境下

1、手动管理引用计数:
在非ARC环境下,需要手动管理引用计数,手动调用retainrelease

// 示例:手动管理引用计数
NSString *manualReference = [[NSString alloc] initWithFormat:@"Hello, World!"];
[manualReference retain]; // 增加引用计数
[manualReference release]; // 减少引用计数

2、使用自动释放池:
在非ARC环境下,可以使用自动释放池来延迟释放对象

// 示例:使用自动释放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSString *autoreleaseReference = [[[NSString alloc] initWithFormat:@"Hello, World!"] autorelease];

[pool drain]; // 手动释放池中的对象

2、iOS中有哪些多线程?有什么区别?

  在iOS中,多线程是一种并发编程的技术,允许应用程序同时执行多个任务。以下是iOS中常用的多线程技术,以及它们之间的一些区别:

  • NSThread:是Objective-C中的一个轻量级多线程类。你可以使用NSThread来创建和管理线程。但需要注意的是,NSThread的使用相对底层(提供了较为基础的线程管理接口),需要手动管理线程的生命周期和线程执行。

a、线程的创建和启动
  需要手动创建线程对象、设置线程的执行方法、启动线程,并在适当的时机手动管理线程的生命周期

// 创建线程对象
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod) object:nil];

// 启动线程
[myThread start];

b、线程同步和通信
  提供了基础的线程同步和通信机制,例如使用performSelector:onThread:withObject:waitUntilDone:方法来在不同线程中执行方法(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), ^{
    // 后台任务完成
    
    // 通知主线程执行操作
    [self performSelector:@selector(taskCompleted) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];
});

3)、线程之间的通信

// 在后台线程中执行任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self performSelector:@selector(taskCompleted) onThread:myThread withObject:nil waitUntilDone:NO];
});

  • GCD:由苹果提供的高级多线程技术,用于管理并发任务。GCD提供了一个简单而强大的API,通过队列来管理任务的执行。GCD负责调度任务,开发者无需手动创建线程。GCD包括了串行队列、并发队列和全局队列等概念。
  • NSOperation/NSOperationQueue:建立在GCD之上的更高级别的抽象。NSOperation定义了一个操作,而NSOperationQueue管理操作的执行。这提供了更多的控制和额外的功能,例如操作之间的依赖关系取消和暂停等。

区别:

  • 抽象程度

    • NSThread:较为底层,需要手动管理线程的创建、启动和销毁。
    • GCD:提供了更高级别的抽象,通过队列管理任务。
    • NSOperationQueue:提供了更高级别的操作管理
  • 手动与自动管理

    • NSThread:手动管理线程的生命周期和执行
    • GCD/NSOperationQueue:自动化的方式来管理并发任务,开发者只需关注任务的执行逻辑。
  • 队列类型

    • GCD/NSOperationQueue:包含了串行队列和并发队列,允许开发者轻松实现并发任务
  • 依赖关系:串行队列和并发队列,同时支持设置最大并发数。

    • GCD/NSOperationQueue:支持任务之间的依赖关系,可以指定一个任务在另一个任务完成后才执行。使得任务之间的协作变得更为灵活。

3、Block是什么?有哪些?

  Block是一种在C语言基础上构建的Objective-C特性,允许你创建一个封装了一段代码的对象,可以在程序中传递执行。Block也被称为闭包,它捕获了其定义时的环境,使得在执行时可以访问这个环境。
语法如下

returnType (^blockName)(parameterTypes) = ^returnType(parameters) {
    // block 体
};

1、无参数无返回值的Block

// 声明并定义一个无参数无返回值的 Block
void (^simpleBlock)(void) = ^{
    NSLog(@"This is a simple block.");
};

// 调用 Block
simpleBlock();

2、带参数的Block

// 声明并定义一个带参数的 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作为方法参数

// 声明一个方法,参数为 Block
- (void)performOperationWithBlock:(void (^)(void))operationBlock {
    NSLog(@"Before Block");
    // 执行传入的 Block
    operationBlock();
    NSLog(@"After Block");
}

// 调用方法,传入 Block
[self performOperationWithBlock:^{
    NSLog(@"Inside the Block");
}];

Block在异步线程回调函数动画集合操作等场景中被广泛使用。

4、iOS中如何保存数据?有哪些持久化存储机制?

1、UserDefaults:存储小量数据轻量级存储方式,适用于简单的配置信息用户偏好设置等。它基于键值对的方式进行数据存储。

// 保存数据到UserDefaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"John" forKey:@"UserName"];
[userDefaults synchronize]; // 可选的,手动同步UserDefaults

// 从UserDefaults读取数据
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *userName = [userDefaults objectForKey:@"UserName"];

2、文件存储:可以使用NSDataNSString等进行数据的读写。

// 保存数据到文件
NSString *dataString = @"Hello, World!";
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"data.txt"];
[dataString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];

// 从文件中读取数据
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"data.txt"];
NSString *dataString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

3、Core Data:一个面向对象的数据持久化框架,适用于处理大量结构化的数据。提供了对象关系映射查询功能。

// 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数据库:可以直接使用SQLite API
或者使用封装库(FMDB)来实现

// 使用 FMDB 操作 SQLite 数据库
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中常用的一些持久化存储机制,选择适当的方式取决于应用程序的需求和数据的性质。

  • UserDefaults适用于小量数据
  • 文件存储适用于简单文本或二进制数据
  • Core Data适用于复杂的数据模型
  • SQLite则适用于需要关系型数据库的情况。

5、什么是浅拷贝?什么是深拷贝?

  浅拷贝深拷贝是在编程中经常遇到的两个概念,它们描述了在复制对象或数据结构时所采用的不同策略。

  • 浅拷贝:创建一个新的对象,但不复制对象中的所有内容只复制对象的引用,而不复制引用指向的内容。因此,原始对象和浅拷贝后的对象共享相同的子对象。如果修改了原始对象中的共享子对象,浅拷贝后的对象也会受到影响
#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类实现了NSCopying协议,并重写了copyWithZone:方法进行浅拷贝。通过copy方法创建的person2对象和person1对象共享相同的hobbies数组,因此修改一个对象的hobbies
数组会影响到另一个对象。

  • 深拷贝:创建一个新的对象,并递归地复制对象中的所有内容,包括对象所引用的其他对象。深拷贝生成的新对象与原始对象完全独立对一个对象的修改不会影响影响到另一个对象
#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:方法,这会递归地复制数组中的每个元素,从而创建了一个全新的hobbies数组,使得修改一个对象的hobbies不会影响到另一个对象。

让我们通过一个生活中的比喻来理解
  假设你制作了一个购物清单,上面列有你需要购买的物品。现在你和你朋友决定共享这个购物清单,以便两人都知道需要购买什么。
浅拷贝:你和朋友共用这一份购物清单。如果你修改了清单上的某个物品,这个变化也会影响到你朋友,因为你们就这一份,按着上面的来购物。
深拷贝:你制作了一份购物清单的副本,你们两个各自拿着一张清单。这个时候,你修改了自己的清单,朋友那是不受到影响的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 一、内存管理 1. 引用计数 OC类中实现了引用计数器,对象知道自己当前被引用的次数。 对象初始化时计数器为1,每...
    邢罗康阅读 1,269评论 0 3
  • iOS基础类 这里包含了去哪儿,滴滴,蚂蚁金服,美团,今日头条,快手以及其它公司的一些面试题,大部分面试官问的重复...
    iOS鑫阅读 638评论 0 19
  • OC的理解与特性 OC作为一门面向对象的语言,自然具有面向对象的语言特性:封装、继承、多态。它既具有静态语言的特性...
    蜗牛上上升阅读 846评论 0 0
  • iOS底层原理 对象底层原理[https://blog.csdn.net/m403180222/article/d...
    zy_64c6阅读 127评论 0 1
  • 1.OC的类可以多重继承吗?可以实现多个接口吗?要想实现类似多重继承如何实现?答:OC不可以实现多重继承。可以实现...
    欧辰_OSR阅读 1,972评论 0 30