在框架中定义有一些特殊的 Layer 可供使用, 这些 Layer 往往具有特定的功能:
链接:https://www.jianshu.com/p/75c702618176
https://www.jianshu.com/p/97d4eea8c23c
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
https://blog.csdn.net/yanerhao/article/details/77829191
https://blog.csdn.net/bravegogo/article/details/86373836
如果是直接把ViewController的view 通过addSubview添加到另一个View,则不会调用viewDidAppear,你需要手动发送viewDidAppear给这个View Controller。
当我向一个UINavigationController压入一个视图控制器的时候,
1. 什么时候会触发viewWillAppear和viewDidAppear?
2. 什么导致了触发viewWillAppear和viewDidAppear失败?
【答】:
当你调用pushViewController:animated把一个视图控制器压入UINavigationController的时候,UINavigationController会自动调用这些方法。相似的,当你tabs时,UITabBarController会直接调用这些方法,当你使用presentModalViewController时也会调用方法。当一个视图控制器的视图被添加到一个window中时也会调用这些方法。我在这些情况下没有遇到过这些方法调用失败的情况。
记住,这些方法只在这些特定的情况下控制器pushed或presented的时候被调用。在其它的情况下不会被调用,比如你添加你的视图控制器的视图作为一个视图的子视图而不是作为window的子视图。苹果官方文档说视图控制器仅用于全屏显示的视图,典型的是使用上面提到的方法。可以忽略苹果的建议使一个视图控制器关联另一个视图控制的的视图作为子视图,但是你需要在作为容器的视图控制器中手动的调用嵌套控制器的viewWillAppear和viewDidAppear。
https://blog.csdn.net/hherima/article/details/49514191
解决方式:
addChildViewController 与 didMoveToParentViewController
1.将newVC添加到Controller的childViewController,建立父子关系。
可以通过parentViewController访问newVC的父类,调用addChildViewController方法系统会自动调用willMoveToParentViewController:方法。
2.将newVC的view加到父类的view上去,当然还要确定view在父类view上的frame。
3.调用child的 didMoveToParentViewController: ,以通知child,完成了父子关系的建立。
链接:https://www.jianshu.com/p/2148f9cfa010
來源:简书
https://www.jianshu.com/p/7bbbe5d55440
https://blog.csdn.net/wks_lovewei/article/details/80563106 iOS底层原理总结 - 探寻block的本质
https://blog.csdn.net/Deft_MKJing/article/details/53143076 iOS Block源码分析系列
https://blog.csdn.net/deft_mkjing/article/details/53149629 iOS Block源码分析系列(二)————局部变量的截获以及__block的作用和理解
https://www.jianshu.com/p/3e819d731c79
1.全局静态变量
在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。
1)内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)
2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是 任意的,除非他被显示初始化)
3)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。
定义全局静态变量的好处:
<1>不会被其他文件所访问,修改
<2>其他文件中可以使用相同名字的变量,不会发生冲突。
2.局部静态变量
在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。
1)内存中的位置:静态存储区
2)初始化:未经初始化的局部静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
3)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。
注:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
当static用来修饰全局变量的时候,它就改变了全局变量的作用域(在声明他的文件之外是不可见的),但是没有改变它的存放位置,还是在静态存储区中。
3.Static修饰的函数
在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件中可见,不能被其他文件所用。
定义静态函数的好处:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突
<2> 静态函数不能被其他文件所用。
存储说明符auto,register,extern,static,对应两种存储期:自动存储期和静态存储期。
auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。
关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。
https://blog.csdn.net/shihuboke/article/details/79214171
https://tech.meituan.com/DiveIntoCategory.html
参考:https://blog.csdn.net/Hello_Hwc/article/details/80094632
一、__bridge
进行OC指针和CF指针之间的转换,不涉及对象所有权转换。
那么,什么叫做对象的所有权转换呢?再理解之前记住两点:
Foundation对象是由ARC管理的(这里不考虑MRC的情况),你不需要手动retain和release。
Core Foundation的指针是需要手动管理生命周期。
举例:OC -> CF,所有权在Foundation,不需要手动管理
NSString * str = [NSString stringWithFormat:@"%ld",random()];
CFStringRef cf_str = (__bridge CFStringRef)str;
NSLog(@"%ld",(long)CFStringGetLength(cf_str));
举例:CF -> OC,所有权在CF,需要手动管理内存
CFStringRef cf_str = CFStringCreateWithFormat (NULL, NULL, CFSTR("%d"), rand());
NSString * str = (__bridge NSString *)cf_str;
NSLog(@"%ld",(long)str.length);
//这一行很有必要,不然会内存泄漏
CFRelease(cf_str);
二、__bridge_retained
将一个OC指针转换为一个CF指针,同时移交所有权,意味着你需要手动调用CFRelease来释放这个指针。这个关键字等价于CFBridgingRetain函数。
举例:
NSString * str = [NSString stringWithFormat:@"%ld",random()];
CFStringRef cf_str = (__bridge_retained CFStringRef)str;
NSLog(@"%ld",(long)CFStringGetLength(cf_str));
CFRelease(cf_str);
三、__bridge_transfer
将一个CF指针转换为OC指针,同时移交所有权,ARC负责管理这个OC指针的生命周期。这个关键字等价于CFBridgingRelease
举例:
CFStringRef cf_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), rand());
NSString * str = (__bridge_transfer NSString *)cf_str;
NSLog(@"%ld",(long)str.length);
四、总结一句话,所有权在Foundation,则不需要手动管理内存;所有权在CF,需要调用CFRetain/CFRelease来管理内存。
1 数据类型的变化:
数据类型里面,NSInteger在32位时等同于int,在64位时等同于long,而这个数据结构使用非常广,非常多不规范的时候会直接和int替换使用,在32位是毫无问题,但在64位时。这就是隐患了。CGFloat也有相同的问题,所以代码的检查改动必须细致。
至于对齐,假设使用了偏移量来訪问struct的项,那么须要认真细致的检查,其余的还算好。当然假设你用了malloc,那么也请检查一下分配的内存大小。建议是多使用sizeof来帮助计算。
还有,针对存储的文件。比方存储在iCloud上的文件。你无法确定是一个32位应用还是64位的应用会去訪问。那么请一定把数据内容的解释写成一模一样。
2 方法调用上的变化
3 汇编的不同, 由于是不同的指令集。汇编当然会不同。只是我们一般的应用不会用到汇编,所以这一项比較少遇到。
4 第三方库, 我们项目中使用的第三方库肯定须要支持64位系统。否则还是白搭。 所以大家在升级时须要检查自己使用的第三方的库。看是否已经有64位的版本号出现了。
1.n个节点的二叉树一共有((2n)!)/(n! * (n+1)!)种
2.n层二叉树的第n层最多为2^(n-1)个
3.二叉树节点计算公式 N = n0+n1+n2,度为0的叶子节点比度为2的节点数多一个。N=1*n1+2*n2+1
4.对任何一棵二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1
5.具有n个节点的完全二叉树的深度为log2(n) + 1
6 二叉树类型 https://www.cnblogs.com/love-yh/p/7423301.html
原文:https://blog.csdn.net/qq_33401691/article/details/78021446
https://www.jianshu.com/p/14a5089e0456
kCFRunLoopEntry: NSLog(@"进入");
kCFRunLoopBeforeTimers: NSLog(@"即将处理Timer事件");
kCFRunLoopBeforeSources: NSLog(@"即将处理Source事件");
kCFRunLoopBeforeWaiting: NSLog(@"即将休眠");
kCFRunLoopAfterWaiting: NSLog(@"被唤醒");
kCFRunLoopExit: NSLog(@"退出RunLoop");
https://blog.csdn.net/u012581760/article/details/53008454
https://www.cnblogs.com/codingmengmeng/p/6046481.html
https://blog.csdn.net/houyichaochao/article/details/80857632
https://www.cnblogs.com/junhuawang/p/7598236.html
三种模式。默认
https://www.jianshu.com/p/6c671b3f409e
https://www.jianshu.com/p/cff0d1b3c915
https://www.jianshu.com/p/f247f8c13b32
https://mp.weixin.qq.com/s/I9F3BP67t2VgCZT98JZ0cw 为什么产生离屏渲染
https://www.jianshu.com/p/9467b424f172
[[UIScreen mainScreen] scale]是计算屏幕分辨率的
https://www.jianshu.com/p/68d1b867af8d
https://blog.csdn.net/Hello_Hwc/article/details/80094632
http://blog.cnbang.net/tech/3386/
https://mp.weixin.qq.com/s/PC9Bb0maBIocvLdrQZd95Q
https://www.jianshu.com/p/02d6f80ac803
https://www.jianshu.com/p/ef1fb33fe2e6
https://www.jianshu.com/p/0042d8eb67c0
http://www.open-open.com/lib/view/open1495626153650.html#articleHeader10 如何打造易扩展的高性能图片组件
http://www.cocoachina.com/ios/20180626/23933.html iOS的5种图片缩略技术以及性能探讨
https://mp.weixin.qq.com/s/KsZyvwIpuTjOVCuWDo7LYw iOS高效图片 IO 框架是如何炼成的
地址: http://www.cocoachina.com/ios/20170503/19165.html
@property (nonatomic, assign, readwrite) id delegate;
声明一个delegate,那么即便delegate指向的对象销毁了,delegate中依然会保存之前对象的地址
即,delegate成为了一个野指针
@property (nonatomic, weak, readwrite) id delegate;
而使用weak,则不会有上述问题,当delegate指向的对象销毁后,delegate = nil。
扩展:
(1)assign “设置方法”只会招待针对“纯量类型”(scalar type,例如CGFloat或NSInteger等)的简单赋值操作。
strong 此特质表明该属性定义了一个拥有关系(owning relationship)。为这种属性设置新值时,设置方法会先保留新值,再释放旧值,然后再将新值设置上去。
(2)weak 此特质表明该属性定义了一种非拥有关系(no owning relationship),为这种属性添加新值时,设置方法即不保留新值,也不保留旧值。此特质同assign类似,然而在改改所指的对象遭到摧毁时,属性值也会清空(nilout).
(3)unsafe_unretained
看名字就知道了 ,不安全 不增加引用计数
NSString* str1 = @"123";
unsafe_unretianed NSString* str2 = str1;
[str1 release];
str2也跟着消失的无影无踪了,指针的地址空间都被清空了,和C++的引用很相似,别名。
@interface Test : NSObject{
int xx ;
}
@property (copy) BlockA block2;
@property (copy) NSString * test;
@property (assign) int z ;
- (void)testFuc;
@end
BlockA block3 = ^{
int xx = 200;
NSLog(@" 全局 %d", xx);
};
@implementation Test
- (void)testFuc{
xx =10;
int yy = 11;
__block int t = 11;
self.test = @"22222";
// stack 栈 --------------------
void(^block1)(void) = ^{
xx = 100;
NSLog(@"栈 %d",xx);
self.test = @"90900990090990990";
// yy = 100; //会编译error
};
block1();
self.z = 4001;
__weak typeof(self) wSelf = self;
// 堆上的 --------------------
self.block2 = ^{
__strong typeof(wSelf) sSelf = wSelf;
xx = 200; //循环引用
NSLog(@"堆上的 %d", xx); //循环引用
sSelf->xx = 201; //这样不会循环引用
NSLog(@"堆上的 %d", sSelf->xx);
// yy = 90; // 会编译 error
t = 100; // 正确
sSelf.z = 400;
NSLog(@"堆上的 %d", sSelf.z);
};
self.block2();
//global 全局 --------------------
block3();
}
@end
扩展:
在Objective-C语言中,一共有3种类型的block:
1._NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量。
2._NSConcreteStackBlock 保存在栈中的block,当函数返回时会被销毁。
3._NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁。
(1)串行队列
dispatch_queue_t concurrentQueue= dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(concurrentQueue, ^(){
NSLog(@"4");
dispatch_sync(concurrentQueue, ^(){
NSLog(@"4.1");
});
});
NSLog(@"5");
输出:1 ,5,4 ,然后死锁
dispatch_queue_t concurrentQueue= dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL);// 串行队列
NSLog(@"1");
dispatch_sync(concurrentQueue, ^(){
NSLog(@"4");
dispatch_sync(concurrentQueue, ^(){
NSLog(@"4.1");
});
});
NSLog(@"5");
输出:1 ,4 ,然后死锁
(2)并行队列
dispatch_queue_t concurrentQueue= dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT);// 并行队列
此时不会死锁。
(3)主线程是串行的
所以,也会死锁
扩展:http://www.jianshu.com/p/44369c02b62a
(4)死锁分析
dispatch_queue_t queue =dispatch_queue_create("abc", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue,^{//taskA
//dosomething
dispatch_sync(queue, ^{//taskB
//啥也干不了
});
});
dispatch_sync函数用于将一个block(任务)提交到队列中同步执行,直到block执行完后,这个函数才会返回。queue是一个串行队列,如果先后加入两个任务,taskA和taskB, 那么只有taskA执行完之后taskB才能执行。如果taskB是在taskA中加进队列的,那么它们依然遵守先进先出原则,即taskA执行完之后taskB才执行,也就是taskB在等待taskA完成。但是因为dispatch_sync的同步特性,taskB执行不完taskA就不算完成,即taskA在等待taskB的完成,这样就发生了死锁。
根据上面那份代码,我们就可以理解下面的代码为什么会阻塞主线程了。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
NSLog(@"hello");
});
mainQueue是系统创建的,在执行上面的代码之前就已经加进去了很多任务
dispatch_queue_t queue = dispatch_queue_create("Main", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//Task A
});
...
dispatch_sync(queue, ^{
//Task N
});
在这N个任务里有一个任务是这样的:
dispatch_sync(queue, ^{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
NSLog(@"hello");
});
});
所以,发生死锁就是必然的了。
如果视图是从nib中加载的,我们应该首先实现initWithCode:因为nib中的对象实例将存储为归档对象。(某一个view设置为自定义的子view)
遍历UIView子视图,找出按钮控件,如果点击在范围内则返回当前控件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
for (UIView *tmpView in self.subviews )
{
if(tmpView.userInteractionEnabled &&[tmpView isMemberOfClass:[UIButton class]])
{
if(CGRectContainsPoint(tmpView.frame,point)) {
return tmpView;
}
}
}
return nil;
}
class NSView 中 @property (copy) NSArray<__kindof NSView *> *subviews;
参考《黑幕背后的Autorelease》
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:@"sunnyxx"];
// str是一个autorelease对象,设置一个weak的引用来观察它
reference = str;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%@", reference); //Console: sunnyxx
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%@", reference); // Console: (null)
}
这个实验同时也证明了viewDidLoad和viewWillAppear是在同一个runloop调用的,而viewDidAppear是在之后的某个runloop调用的。
由于这个vc在loadView之后便add到了window层级上,所以viewDidLoad和viewWillAppear是在同一个runloop调用的,因此在viewWillAppear中,这个autorelease的变量依然有值。
当然,我们也可以手动干预Autorelease对象的释放时机:
- (void)viewDidLoad {
[super viewDidLoad];
@autoreleasepool {
NSString *str = [NSStringstringWithFormat:@"sunnyxx"];
}
NSLog(@"%@", str); // Console: (null)
}
使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];
在普通for循环和for in循环中没有,所以,还是新版的block版本枚举器更加方便。for循环中遍历产生大量autorelease变量时,就需要手加局部AutoreleasePool咯。
NSURLConnection ::
NSMutableURLRequest* request = [[NSMutableURLRequest alloc]initWithURL:self.URL cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:self.timeoutInterval];
[request setHTTPMethod:@"GET"];
self.connection=[[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO];
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];
GCD::
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^(){
[downloader start];
NSLog(@"current worker thread: %@", [NSThread currentThread]);
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
NSLog(@"exit worker thread");
});
8 Cocoa和CoreFoundation定义的标准模式:
NSDefaultRunLoopMode:默认的运行模式,用于大部分操作,除了NSConnection对象事件。
NSConnectionReplyMode:用来监控NSConnection对象的回复的,很少能够用到。
NSModalPanelRunLoopMode:用于标明和Mode Panel相关的事件。
NSEventTrackingRunLoopMode:用于跟踪触摸事件触发的模式(例如UIScrollView上下滚动)。
NSRunLoopCommonModes:是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式。
Cocoa应用默认包含Default、Panel、Event Tracking模式,Core Foundation只包含Default模式,我们可以通过CFRunLoopAddCommonMode添加模式。
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
其中 CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。
1. 通知观察者NSRunloop进入
2. 如果有Timer即将触发时,通知观察者
3. 如果有非Port的Input Sourc即将e触发时,通知观察者
4. 触发非Port的InputSource事件源
5. 如果基于Port的Input Source事件源即将触发时,立即处理该事件,跳转到步骤9
6. 通知观察者当前线程将进入休眠状态
7. 将线程进入休眠状态直到有以下事件发生:基于Port的Input Source被触发、Timer被触发、Run Loop运行时间到了过期时间、Run Loop被唤起
8. 通知观察者线程将要被唤醒
9. 处理被触发的事件:如果是用户自定义的Timer,处理Timer事件后重新启动Run Loop进入步骤2、如果线程被唤醒又没有到过期时间,则进入步骤2、如果是其他Input Source事件源有事件发生,直接处理这个事件
10. 到达此步骤说明Run Loop运行时间到期,或者是非Timer的Input Source事件被处理后,RunLoop将要退出,退出前通知观察者线程已退出
Source 0:
处理如UIEvent,CFSocket这样的事件 (触摸事件、按钮点击事件)。
使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理 signal 状态,然后手动调 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
Source 1:
基于Port的,通过内核和其他线程通信,接收分发系统事件 Mach port驱动,CFMachport,CFMessagePort。 (触摸硬件,通过 Source1 接收和分发系统事件到 Source0 处理)
每个线程(包含主线程)都有一个Runloop。
对于每一个Runloop,系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个像callstack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。
+ (AccountManager *)sharedManager
{
staticAccountManager *sharedAccountManagerInstance = nil; // 静态变量 ,只执行一次。从第二次后,每次进这个方法后,这句都不执行。
staticdispatch_once_t predicate; // 静态变量 ,只执行一次。从第二次后,每次进这个方法后,这句都不执行。
dispatch_once(&predicate, ^{
sharedAccountManagerInstance= [[self alloc] init];
});
return sharedAccountManagerInstance;
}
dispatch_once 函数会执行多次,但是 block只执行一次。
assign “设置方法” 只会执行针对“纯量”的简单赋值操作。
strong 此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。
weak 此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到推毁时,属性值也会清空。
unsafe_unretained 此特质的语义和assign相同,但是它适用于“对象类型”,该特质表达一种“非拥有关系”,当目标对象遭到推毁时,属性值不会自动清空,这一点与weak有区别。
copy此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是设置方法并不保留新值,而是将其“拷贝”。
当属性类型为NSString*时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个NSMutableString类的实例。这个类是NSString的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变”的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”,就应该在设置新属性值时拷贝一份。
@autoreleasepool {
NSString *str = [[NSString alloc] init];
[str retain];
[str retain];
str =@"jxl";
[str release];
[str release];
[str release];
}
1.内存泄露 2.指向常量区的对象不能release。
指针变量str原本指向一块开辟的堆区空间,但是经过重新给str赋值,str的指向发生了变化,由原来指向堆区空间,到指向常量区。
常量区的变量根本不需要释放,这就导致了原来开辟的堆区空间没有释放,照成内存泄露。
retain属性的setter方法是保留新值并释放旧值,然后更新实例变量,令其指向新值。顺序很重要。假如还未保留新值就先把旧值释放了,而且两个值又指向同一个对象,先执行的release操作就可能导致系统将此对象永久回收。
-(void)setName:(NSString *)name
{
[name retain];
[_name release];
_name = name;
}
-(void)setName:(NSString *)name
{
[_name release];
_name = [name copy];
}
dispatch_queue_t concurrentQueue =dispatch_queue_create("my.concurrent.queue",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-2");
});
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"dispatch-barrier");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-3");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-4");
});
dispatch_group_tgroup = dispatch_group_create(); // 某个任务放进 group
dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^{
// 任务代码1
});
dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^{
// 任务代码2
});
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
// 任务全部完成处理 NSLog(@"isover");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (inti = 0; i < 3; i ++) {
dispatch_group_enter(group); // 任务代码i 假定任务 是异步执行block回调
// block回调执行
dispatch_group_leave(group); // block回调执行
}
});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER); dispatch_async(dispatch_get_main_queue(), ^{
// 主线程处理
});
参考:
把一组任务提交到队列中,这些队列可以不相关,然后坚挺这组任务完成的事件。
几个用到的函数
1、dispatch_group_create创建一个调度任务组
func dispatch_group_create() -> dispatch_group_t!
2、dispatch_group_async 把一个任务异步提交到任务组里
func dispatch_group_async(_ group: dispatch_group_t!, _queue: dispatch_queue_t!, _ block: dispatch_block_t!)
参数: group 提交到的任务组,这个任务组的对象会一直持续到任务组执行完毕
queue 提交到的队列,任务组里不同任务的队列可以不同
block 提交的任务
3、dispatch_group_enter/dispatch_group_leave
funcdispatch_group_enter(_ group: dispatch_group_t!)func dispatch_group_leave(_group: dispatch_group_t!)
这两个方法显示的讲任务组中的任务未执行完毕的任务数目加减1,这种方式用在不使用dispatch_group_async来提交任务,注意:这两个函数要配合使用,有enter要有leave,这样才能保证功能完整实现。也可以用这对函数来让一个闭包关联多个Group
4、dispatch_group_notify 用来监听任务组事件的执行完毕
funcdispatch_group_notify(_ group: dispatch_group_t!,
_ queue:dispatch_queue_t!,
_block: dispatch_block_t!)
参数: group监听的任务组
queue 执行完毕的这个闭包所在的队列
block 执行完毕所响应的任务
5、dispatch_group_wait 设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败
long dispatch_group_wait (dispatch_group_t group, dispatch_time_t timeout );
参考:https://www.jianshu.com/p/228403206664
NSTimeIntervalperiod = 1.0; //设置时间间隔
dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer,^{
//在这里执行事件
});
dispatch_resume(_timer);
doubledelayInSeconds = 2.0;
dispatch_time_tpopTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime,dispatch_get_main_queue(), ^(void){
//执行事件
});
(1)默认加入runloop
NSTimer*timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(action:)
userInfo:nilrepeats:NO];
(2)必须加入runloop
NSTimer*timer = [NSTimer timerWithTimeInterval:5
target:self
selector:@selector(timerAction)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
timer是不一定准时的,是有可能被delay的,每次间隔的时间是不一定一样的。
Arepeating timer reschedules itself automatically based on the scheduled firingtime, not the actual firing time.
Forexample, if a timer is scheduled to fire at a particular time and every 5seconds after that, the scheduled
firingtime will always fall on the original 5 second time intervals, even if theactual firing time gets delayed.
If thefiring time is delayed so much that it misses one or more of the scheduledfiring times, the timer is fired
onlyonce for the missed time period. After firing for the missed period, the timeris rescheduled for the next
scheduledfiring time.
简单解读一下:就是说一个repeat的timer,它在创建的时候就把每次的执行时间算好了,而不是真正启动的时候
才计算下次的执行时间。举个例子,假如一个timer在一个特定的时间t激活,然后以间隔5秒的时间重复执行。那么
它的执行操作的时间就应该为t, t+5, t+10,... 假如它被延迟了,
例如,实际上timer在t+2的时候才启动,那么它下次执行的时间还是t+5,而并不是t+2+5,如果很不幸地
在t+5还没有启动,那么它理应该在t执行的操作就跟下一次t+5的操作合为一个了。
至于为什么会延迟呢,这就跟当前线程的操作有关,因为timer是同步交付的,所以假如当前线程在执行很复杂
的运算时,那必须等待运算的完成才能调用timer,这就导致了timer的延迟。
//这里创建timer以每隔1秒执行
[NSTimerscheduledTimerWithTimeInterval:1 target:self selector:@selector(timerDone)userInfo:nil repeats:YES];
//这里在第3秒的时候模拟一个复杂运算
[selfperformSelector:@selector(busyDone) withObject:nil afterDelay:3];
-(void)busyDone
{
//这里模拟线程复杂的运算
for(NSInteger i = 0; i< 0xffffffff;i++){
}
NSLog(@"BusgDone");
}
-(void)timerDone
{
NSLog(@"Timer Run");
}
// 屏幕刷新时调用
//CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。
CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target
发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新
率60次/秒
// 延迟
// iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用
的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。
// 如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
// 使用场景
// 从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
// 2.1创建出displaylink对象
CADisplayLink *displyLink = [CADisplayLink displayLinkWithTarget:selfselector:@selector(reloop)];
// 2.2 将该对象加入循环中
[displyLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// 2.3再不需要时释放(停止循环)
[displyLink invalidate];
displyLink = nil;
· NULL是宏,是对于C语言指针而使用的,表示空指针
· nil是宏,是对于Objective-C中的对象而使用的,表示对象为空
· Nil是宏,是对于Objective-C中的类而使用的,表示类指向空
· NSNull是类类型,是用于表示空的占位对象,与JS或者服务端的null类似的含意
NSRunLoop的作用在于有事情做的时候使的当前NSRunLoop的线程工作,没有事情做让当前NSRunLoop的线程休眠。
NSTimer默认添加到当前NSRunLoop中,也可以手动制定添加到自己新建的NSRunLoop。
NSRunLoop就是一直在循环检测,从线程start到线程end,检测inputsource(如点击,双击等操作)同步事件,检测timesource同步事件,检测到输入源会执行处理函数,首先会产生通知,corefunction向线程添加runloop observers来监听事件,意在监听事件发生时来做处理。
在单线程的app中,不需要注意Run Loop,但不代表没有。程序启动时,系统已经在主线程中加入了Run Loop。它保证了我们的主线程在运行起来后,就处于一种“等待”的状态(而不像一些命令行程序一样运行一次就结束了),这个时候如果有接收到的事件(Timer的定时到了或是其他线程的消息),就会执行任务,否则就处于休眠状态。
runloopmode是一个集合,包括监听:事件源,定时器,以及需通知的runloop observers
模式包括:
default模式:几乎包括所有输入源(除NSConnection) NSDefaultRunLoopMode模式
mode模式:处理modal panels
connection模式:处理NSConnection事件,属于系统内部,用户基本不用
event tracking模式:如组件拖动输入源 UITrackingRunLoopModes 不处理定时事件
common modes模式:NSRunLoopCommonModes 这是一组可配置的通用模式。将inputsources与该模式关联则同时也将input sources与该组中的其它模式进行了关联。
每次运行一个run loop,你指定(显式或隐式)run loop的运行模式。当相应的模式传递给run loop时,只有与该模式对应的input sources才被监控并允许run loop对事件进行处理(与此类似,也只有与该模式对应的observers才会被通知)
例:
1)在timer与table同时执行情况,当拖动table时,runloop进入UITrackingRunLoopModes模式下,不会处理定时事件,此时timer不能处理,所以此时将timer加入到NSRunLoopCommonModes模式(addTimer forMode)
2)在scroll一个页面时来松开,此时connection不会收到消息,由于scroll时runloop为UITrackingRunLoopModes模式,不接收输入源,此时要修改connection的mode
1 |
[scheduleInRunLoop:[NSRunLoop currentRunLoop]forMode:NSRunLoopCommonModes]; |
关于-(BOOL)runMode:(NSString )mode beforeDate:(NSDate )date;方法
指定runloop模式来处理输入源,首个输入源或date结束退出。
暂停当前处理的流程,转而处理其他输入源,当date设置为[NSDate distantFuture]-(将来,基本不会到达的时间),所以除非处理其他输入源结束,否则永不退出处理暂停的当前处理的流程。
NSTimer 的定时器是在 RunLoop 中实现的,由于RunLoop在处理各种任务,所以会造成计时器不够准确,有时候会相对慢一些,有没有什么方法会让计时变得准确?有,使用 GCD 的计时器方法会让计时器变得相对准确,而且GCD不受RunLoop的 Mode 影响。
http://www.cnblogs.com/feng9exe/p/8848684.html
https://www.cnblogs.com/feng9exe/p/8848663.html
http://m.blog.csdn.NET/majiakun1/article/details/73421480
UIView
是可以响应事件的,但是CALayer
不能响应事件UIView
主要负责管理内容,而CALayer
主要负责渲染和呈现。如果没有CALayer
,我们是看不到内容的。CALayer
维护着三个layer tree
,分别是presentLayer Tree
、modeLayer Tree
、Render Tree
,在做动画的时候,我们修改动画的属性,其实是修改presentLayer
的属性值,而最终展示在界面上的其实是提供UIView
的modelLayer
。SubLayers
,View 内部有 SubViews
.但是 Layer 比 View 多了个AnchorPoint
CALayerDelegate
,View 的显示内容取决于内部的 CALayer 的 display
actionForLayer:forKey:
向 View请求相应的action
(动画行为)layer tree
,分别是 presentLayer Tree
(动画树),modeLayer Tree
(模型树), Render Tree
(渲染树),在做 iOS动画的时候,我们修改动画的属性,在动画的其实是 Layer 的 presentLayer的属性值,而最终展示在界面上的其实是提供 View的modelLayer
作者:不简单的风度
链接:https://www.jianshu.com/p/ed40da9303b1
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
A、几个易混淆的方法 :http://blog.csdn.net/bravegogo/article/details/79241188
B、UIView在AutoLayout下的布局过程 :https://blog.csdn.net/haungcancan/article/details/52996789
http://www.cocoachina.com/ios/20150512/11805.html
http://blog.csdn.net/bravegogo/article/details/79391728
http://blog.csdn.net/bravegogo/article/details/79511073
objc在向一个对象发送消息时,发生了什么?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到对象实际所属的类,然后在该类中的方法列表及父类方法列表中寻找方法并运行。如果在最顶层的父类中依然没找到方法,objc会调用resolveInstanceMethod:方法,让我们动态为该对象添加一个函数实现;如果在该方法中仍没有找到对应的方法,objc会调用forwardingTargetForSelector方法,将该消息转发给其他对象;如果在调用该方法后仍没找到对应方法,首先会调用methodSignatureForSelector方法,获得需要实现函数的参数和返回值类型,如果该消息返回nil则调用doesNotRecognizeSelector方法,程序崩溃,如果返回了函数签名,runtime会创建NSInvocation对象,并调用forwardInvocation方法给目标对象。
参考:https://www.jianshu.com/p/f9bd98ad5b05
http://www.cocoachina.com/ios/20161129/18216.html
https://www.jianshu.com/p/7f68a3d5b07d
https://blog.csdn.net/bravegogo/article/details/81866147
https://blog.csdn.net/bravegogo/article/details/84108038
1. Quartz2D是CoreGraphics的一部分API的抽象,不是实际存在的.framework
2. CoreGraphics定义了颜色、位置、字体、路径、图片等UIKit的常见属性。是构成UIKit的基石。
3. QuartzCore和CoreAnimation是雌雄同体的同义词。
4. CoreAnimation定义了动画类来对layer做动画,定义了layer来呈现内容。定义了仿射变换来做3D动画。
5. CoreImage定义了滤镜,来对图片进行颜色过滤混合等操作。
6.GPUImage是一个著名的图像处理开源库,它让你能够在图片、视频、相机上使用GPU加速的滤镜和其它特效。与CoreImage框架相比,可以根据GPUImage提供的接口,使用自定义的滤镜。