第一章、自动引用计数
1、内存管理/引用计数
- OC中通过引用计数来进行内存管理(其他的内存管理方式有垃圾回收等)
2、内存管理原则
- 2.1:自己生成的对象自己持有
- 使用 alloc new copy mutableCopy 开头的方法名生成的对象,自己持有(其中以..开头是指如:allocMyObject、newObjec、copyThis等驼峰形式的方法名,而alloccate、newer等不在此列)
- 2.2:非自己生成的对象,自己也能持有
- 可以通过 retain 方法来持有该对象
- 2.3:自己持有的对象,自己释放,不是自己持有的对象,自己不用管
- 通过上述两种方式持有的对象,不需要的时候,需要通过release方法进行释放,不是自己通过上述方法持有的对象,不能调用release方法
- 2.4:不通过上面方法获得的对象,同时自己不持有,这时候通过autorelease 进行管理
+(objc*) getObject { id obj = [[objc alloc] init]; [obj autorelease]; return obj; }
- 2.5:alloc/retain/release/dealloc总结
- 通过引用计数表(散列表),来管理对象的引用计数
- 使用allco或者rerain后,该对象的引用计数会+1
- 使用release 后,对象的引用计数会-1
- 当应用计数为0的时候会调用dealloc方法,废弃对象,释放内存
3、ARC规则
-
3.1:所有权修饰符
- 在ARC下,OC对象都必须有所有权修饰符所修饰,这就是告诉编译器,当前对象的内存管理原则,修饰符包括:_ _strong, _ _ weak, _ _unsafe_unretain, _ _autoreleasing
- _ _strong 为默认的修饰符,具体不详细介绍
- _ _weak 为避免循环引用产生内存泄漏问题而产生的
- _ _unsafe_unretain 同 _ _weak性质相同,但是_weak引用的对象在被释放后,变量会被置为nil, 而 _unsafe_unretain不会
- _ _ autoreleasing,从内存管理的原则中我们知道,在ARC无效的时候,通过alloc new copy mutableCopy以外的方法得的对象,会通过调用autorelease方法被注册到autoreleaspool中,在ARC下用autoreleasing修饰符就是为了达到这个效果,但是同strong一样,不需要显示的声明这个修饰符,在符合情况时, 系统会自动将变量加入到autoreleaspool中的。还有一种情况是写回传:因为上面的内存管理原则,下面的代码中error获得对象非自己创建,所以需要加入到autoreleaspool中,所以需要传递autoreleasing 修饰的参数,就产生了下面的过程
NSError * error = nil; NSString * string =[ [NSString alloc]initWithContentsOfURL“/path/file.text” encoding:utf8 error: &error]; //编译器将上面的代码转换为 NSError _ _strong * error = nil; NSError _ _autoreleasing * tempError = error; NSString * string =[ [NSString alloc]initWithContentsOfURL”/path/file.text” encoding:utf8 error: &tempError]; error = tempError //error 被加入到自动释放池中,一直保持到自动释放池释放为止
- 在ARC下,OC对象都必须有所有权修饰符所修饰,这就是告诉编译器,当前对象的内存管理原则,修饰符包括:_ _strong, _ _ weak, _ _unsafe_unretain, _ _autoreleasing
4:修饰符的内部实现略...
第二章、Block
1:block简介及基本用法
- 定义:block是带有自动变量值的匿名函数
- 其他详见block详解
2:Block实质
- 略
- Block被当做一个C语言的匿名函数来处理,同时他也是一个oc类型的对象,内部有isa指针变量,至于isa是啥:
1、When a new object is created, memory for it is allocated, and its instance variables are initialized. First among the object’s variables is a pointer to its class structure. This pointer, called isa, gives the object access to its class and, through the class, to all the classes it inherits from.
2、isa means “is a”. Every Objective-C object (including every class) has an isa pointer. The runtime follows this pointer to determine what class an object is, so it knows what selectors the object responds to, what its super class is, what properties the object has, and so on. - 截获自动变量,block只截获在在内部调用的变量的值,将这些截获的变量保存在block内部结构体内
第三章、GCD
1:API
-
1.1:Dispatch Queue
- 开发者定义想要执行的任务,并追加到任务队列中,在下面的代码中,block内部为想要执行的任务,queue为执行任务的队列,这样该任务就能在另外的线程中执行了
dispatch_async(queue, ^{ //想要执行的任务 });
- Dispatch Queue分为Serial Dispatch Queue(串行)和Concurrent Dispatch Queue(并发)两种
- Serial Dispatch Queue:串行队列中同一时间只能执行一个任务,当前任务执行完后才能执行下一个任务,在串行队列中只有一个线程,当多个线程更新同一个资源,引发资源竞技的时候可以使用串行队列。
- Concurrent Dispatch Queue:允许多个任务在同一个时间同时进行,在并发队列中有多个线程,具体线程的个数是由系统根据当前的心痛状态及CPU核数等因素自行决定的。
- 串行队列的任务一定是按开始的顺序结束,而并发队列的任务并不一定会按照开始的顺序而结束
- 创建:通过GCD提供的API创建队列
//串行 dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.ios.queue", NULL); //并发 dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.ios.apple", DISPATCH_QUEUE_CONCURRENT);
- Main Dispatch Queue, Global Dispatch Queue
- 如果不通过create进行创建,我们也可以直接用系统给我们提供的队列Main Dispatch Queue, Global Dispatch Queue。其中Main 为在主线程中执行的队列,所以他是一个串行队列,Global Dispatch Queue为在主线程外运行的并发队列,同时Global Dispatch Queue还有HIGH,DEFAULT, LOW等几个优先级别
dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //举例用法 dispatch_async(global, ^{ //do something dispatch_async(mainQueue, ^{ // return mainThread, update UI }) });
-
1.2:dispatch_set_target_queue
- 改变队列的优先级,通过dispatch_queue_create创建的串行或并行队列的优先级均为default,可以通过这个方法改变这个优先级
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("mySerialQueue", NULL); dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); //将mySerialDispatchQueue的优先级由默认的default改为BACKGROUND dispatch_set_target_queue(mySerialDispatchQueue, global);
这个方法还可以改变一些其他的属性
-
1.3:dispatch_after
- 一段时间后将任务追加到任务队列中去
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3); dispatch_after(time, dispatch_get_main_queue(), ^{ //3秒后追加到主线程中的任务 });
上面的列子3秒后追加到主线程中的任务,因为主线程中的runloop大概循环时间为1/61秒,所以这个任务最慢在3+1/61秒后执行。
-
1.4 dispatch group
- 当有多个任务执行完成后,想做统一处理,这时候可以用串行队列,但是当有多个队列,或者是并行队列中有多个任务的时候,就需要用到 dispatch group了。
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, global, ^{ //任务1 }); dispatch_group_async(group, global, ^{ //任务2 }); dispatch_group_async(group, global, ^{ //任务3 }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //总结任务 });
另外也可以用dispatch_group_wait 代替dispatch_group_notify,来等待任务全部结束,但是不推荐这样用
-
1.5:dispatch_barrier_async
- 用于在一连串并发队列中设置一个障碍,在障碍前面的任务执行完成后,执行了障碍中的任务后,才能执行障碍后的任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任务1"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任务2"); }); dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"插入栅栏:执行完成任务1,2后,执行插入,在执行任务3,4"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任务3"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"任务4"); });
-
1.6:dispatch_once
- 多线程状态下,保证代码只执行一次,多在生成单例的时候使用
+ (instancetype)sharedManager { static Manager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedManager = [[Manager alloc] init]; }); return sharedManager; }
网友评论