参考文档链接
OC是一门动态语言的根本是Runtime的存在,runtime可以提供得创建类和对象、进行消息传递和转发能力。
一个对象的方法像这样[obj foo],编译器转成消息发送objc_msgSend(obj, foo),Runtime时执行的流程是这样的:
但这种实现有个问题,效率低。但一个class往往只有 20%的函数会被经常调用,可能占总调用次数的 80%。每个消息都需要遍历一次objc_method_list并不合理。如果把经常被调用的函数缓存下来,那可以大大提高函数查询的效率。这也就是objc_class中另一个重要成员objc_cache做的事情 - 再找到foo之后,把foo的method_name作为key,method_imp作为value给存起来。当再次收到foo消息的时候,可以直接在cache里找到,避免去遍历objc_method_list。从前面的源代码可以看到objc_cache是存在objc_class结构体中的。
参考链接,文章讲解了详细的原理和源码分析。
{
/// 1. 通知Observers,即将进入RunLoop
/// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
do {
/// 2. 通知 Observers: 即将触发 Timer 回调。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 4. 触发 Source0 (非基于port的) 回调。
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 6. 通知Observers,即将进入休眠
/// 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
/// 7. sleep to wait msg.
mach_msg() -> mach_msg_trap();
/// 8. 通知Observers,线程被唤醒
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
/// 9. 如果是被Timer唤醒的,回调Timer
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
/// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
/// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
} while (...);
/// 10. 通知Observers,即将退出RunLoop
/// 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
class AutoreleasePoolPage {
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
isa swizzling
方法,通过runtime
动态创建一个中间类,继承自被监听的类。isa
指针指向这个中间类,同时重写改类的Class
方法,使得该类的Class
方法返回自己而不是isa
的指向。setter
方法,调用添加进来的方法,然后给当前中间类的父类也就是原类的发送setter
消息。具体可参考链接
参考链接链接
2种获取方式:一个串行队列对应只有一个线程,因此同时只能执行一个操作,先追加的操作先执行。执行很多操作的时候就好像人们排队买东西一样,先来后到。
//1.手动创建串行队列
dispatch_queue_t mySerialQueue = dispatch_queue_create("com.gcd.queueCreate.mySerialQueue", NULL);
//2.主队列是串行队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
一个并行队列可以有多个线程,同时可以执行多个操作。在执行多个操作的时候,执行顺序会根据操作内容和系统状态发生变化。
//1.手动创建并行队列
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.gcd.queueCreate.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
//2.全局队列是并行队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
定义:字串:连续无重复的字符集
例如:一个字符串 awbcdewgh
他的子串: awbc、awbcd、awbcde …很多个子串 ,但是都是连续在一起 。
他的子序列: abc 、abcd、 abcde … 很多个子序列 ,但是子序列中的字符在字符串中不一定是连在一起的,而是删除其中若干个, 但是子序列一定是单调的(即字符之间ASCII单调递增或单调递减,相对顺序不能改变)
- (void)lengthOfLongestSubstring:(NSString *)tmpString{
NSString *str = tmpString;
NSMutableSet *set = [NSMutableSet new];
long n = str.length;
//length:长度,index:起始位置
int length = 0, i = 0, j = 0 ,index = 0;
while (i < n && j < n) {
//截取单个字符判断
if (![set containsObject:[str substringWithRange:NSMakeRange(j,1)]]) {
//添加当前字符
[set addObject:[str substringWithRange:NSMakeRange(j++, 1)]];
if (j - i > length) {
length = j - i;
index = i;
NSLog(@"lenght:%d---j:%d---i:%d",length,j,i);
}
}else{
[set removeObject:[str substringWithRange:NSMakeRange(i++, 1)]];
}
}
NSString * result = [str substringWithRange:NSMakeRange(index, length)];
NSLog(@"%@",result);
}
产生的几种情况:
检测内存泄露的方法
Block 的内存管理分两种情况,如果是 MRC 其实可以使用 __Block,这样 Block 内部截取的变量的引用计数不会 +1,也就无需程序员动手管理了。block不能修改局部变量的值,若修改需要加__block 修饰。
参考链接
参考链接
因为UIKit框架是线程不安全的。把UIKit设计成线程安全并不会带来太多的便利,也不会提升太多的性能表现,甚至会因为加锁解锁而耗费大量的时间。事实上并发编程也没有因为UIKit是线程不安全而变得困难,我们所需要做的只是要确保UI操作在主线程进行就可以了。所有UI操作在串行队列中就可以了,非主线程异步绘制UI无法满足60fps刷新率
NSCopying是一个与对象拷贝有关的协议。如果想让一个类的对象支持拷贝,就需要让该类实现NSCopying协议。NSCopying协议中的声明的方法只有一个- (id)copyWithZone:(NSZone *)zone。当我们的类实现了NSCopying协议,通过类的对象调用copy方法时,copy方法就会去调用我们实现的- (id)copyWithZone:(NSZone *)zone方法,实现拷贝功能
基于XXX
参考链接
这个方案是Uber 骑手App模块化开发的一个方案。
参考链接
iOS中有Disk I/O Throttle,Memory I/O Throttle,和Network I/O Throttle
Global Queue有哪几种优先级:Default,Low,High,BACKGROUND(I/O Throttle)
应用场景:
button.rx_tap
.throttle(0.5, MainScheduler.instance)
.subscribeNext { _ in
print("Hello World")
}
.addDisposableTo(disposeBag)
可自行添加…
childController.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImage] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
Off-Screen Rendering意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
触发方式:
这个问的其实是数据结构中的二叉树,查找一个普通二叉树中两个节点最近的公共祖先问题
async非对称加密