明确通知在哪个线程中发出
,那么处理接受到通知的方法也在这个线程中调用异步
线程发的通知,那么可以
执行比较耗时的操作;主线程
发的通知,那么就不可以
执行比较耗时的操作weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。
为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;
然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)
runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。
用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,
假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。
// 模拟下weak的setter方法,大致如下
- (void)setObject:(NSObject *)object
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];
}
父类的
成员变量和自己的
成员变量都会存放在该对象所对应的存储空间中isa指针
,指向他的类对象
,类对象中存放着本对象的如下信息Objective-C 对象的结构图 |
---|
ISA指针 |
根类(NSObject)的实例变量 |
倒数第二层父类的实例变量 |
... |
父类的实例变量 |
类的实例变量 |
类方法
列表,根元类的isa指针指向自己,superclass指针指向NSObject类
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
这个题目主要是考察关于objc中对 self 和 super 的理解:
编译器标示符
,和 self 是指向的同一个消息接受者当前类
的方法列表中开始找,如果没有
,就从父类中再找
;调用[self class] 时,会转化成 objc_msgSend函数
id objc_msgSend(id self, SEL op, ...)
调用 [super class]时,会转化成 objc_msgSendSuper函数
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一个参数是 objc_super
这样一个结构体,其定义如下
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
objc Runtime开源代码对- (Class)class方法的实现
-(Class)class {
return object_getClass(self);
}
名称
,方法实现
,以及参数类型
,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.不能
访问成员变量不能
直接调用对象方法可以调用类方法(通过类名)
runtime Associate
方法关联的对象,需要在主对象dealloc的时候释放么?均不需要
晚很多
,它们会在被 NSObject -dealloc 调用的 object_dispose()
方法中释放1.调用 -release :引用计数变为零
* 对象正在被销毁,生命周期即将结束.
* 不能再有新的 __weak 弱引用,否则将指向 nil.
* 调用 [self dealloc]
2. 父类调用 -dealloc
* 继承关系中最直接继承的父类再调用 -dealloc
* 如果是 MRC 代码 则会手动释放实例变量们(iVars)
* 继承关系中每一层的父类 都再调用 -dealloc
3. NSObject 调 -dealloc
* 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
4. 调用 object_dispose()
* 为 C++ 的实例变量们(iVars)调用 destructors
* 为 ARC 状态下的 实例变量们(iVars) 调用 -release
* 解除所有使用 runtime Associate方法关联的对象
* 解除所有 __weak 引用
* 调用 free()
非常危险
的事,这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事实例变量
?能否向运行时创建的类中添加实例变量
?为什么?不能
向编译后得到的类中增加实例变量
;能
向运行时创建的类中添加实例变量
;不能
向存在的类中添加实例变量
可以
添加实例变量
,调用 class_addIvar函数,但是得在调用objc_allocateClassPair之后
,objc_registerClassPair之前
,原因同上。唯一
的一个RunLoop对象与之对应的自动创建并启动
手动创建
子线程的RunLoop创建步骤如下:
懒加载,只创建一次
)获得RunLoop对象后要调用run
方法来启动一个运行循环
// 启动RunLoop
[[NSRunLoop currentRunLoop] run];
RunLoop的其他启动方法
// 第一个参数:指定运行模式
// 第二个参数:指定RunLoop的过期时间,即:到了这个时间后RunLoop就失效了
[[NSRunLoop currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate distantFuture]];
kCFRunLoopDefaultMode
:App的默认Mode
,通常主线程
是在这个Mode下运行,对应OC中的:NSDefaultRunLoopMode
UITrackingRunLoopMode
:界面跟踪
Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响kCFRunLoopCommonModes
:这是一个标记
Mode,不是一种真正的Mode,事件可以运行在所有标有common modes标记的模式中,对应OC中的NSRunLoopCommonModes
,带有common modes标记的模式有:UITrackingRunLoopMode和kCFRunLoopDefaultMode启动
App时进入的第一个 Mode,启动完成后就不再使用
NSDefaultRunLoopMode
模式下的,当滑动页面上的列表时,进入了UITrackingRunLoopMode
模式,这时候timer就会停止可以修改timer的运行模式为NSRunLoopCommonModes
,这样定时器就可以一直运行了
以下是我的笔记补充:
子线程
中通过scheduledTimerWithTimeInterval:...方法
来构建NSTimer仅仅
是创建RunLoop对象,并不会
主动启动RunLoop,需要再调用run方法
来启动如果在主线程
中通过scheduledTimerWithTimeInterval:...方法
来构建NSTimer,就不需要主动启动RunLoop对象,因为主线程的RunLoop对象在程序运行起来就已经被启动了
// userInfo参数:用来给NSTimer的userInfo属性赋值,userInfo是只读的,只能在构建NSTimer对象时赋值
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run:) userInfo:@"ya了个hoo" repeats:YES];
// scheduledTimer...方法创建出来NSTimer虽然已经指定了默认模式,但是【允许你修改模式】
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 【仅在子线程】需要手动启动RunLoop对象,进入运行循环
[[NSRunLoop currentRunLoop] run];
本质:
内部就是do-while循环
,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer;唯一
一个RunLoop对象与之对应,主线程的RunLoop默认已经启动,子线程的RunLoop需要手动启动
;只能
指定其中一个 Mode,这个Mode被称作 CurrentMode,如果需要切换Mode,只能退出
Loop,再重新指定
一个Mode进入,这样做主要是为了隔离不同Mode中的Source、Timer、Observer,让其互不影响;手动干预
释放时机、系统自动
去释放三个状态
会处理自动释放池,通过打印代码发现有两个Observer监听到状态值为:1和160(32+128)kCFRunLoopEntry(1)
// 第一次进入会创建
一个自动释放池kCFRunLoopBeforeWaiting(32)
// 进入休眠状态前先销毁
自动释放池,再创建
一个新
的自动释放池kCFRunLoopExit(128)
// 退出RunLoop时销毁
最后一次创建的自动释放池objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
DISPATCH_QUEUE_CONCURRENT
或者 串行DISPATCH_QUEUE_SERIAL
并发队列
才起作用异步执行
2个耗时的操作// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 往队列组中添加耗时操作
dispatch_group_async(group, queue, ^{
// 执行耗时的异步操作1
});
// 往队列组中添加耗时操作
dispatch_group_async(group, queue, ^{
// 执行耗时的异步操作2
});
// 当并发队列组中的任务执行完毕后才会执行这里的代码
dispatch_group_notify(group, queue, ^{
// 如果这里还有基于上面两个任务的结果继续执行一些代码,建议还是放到子线程中,等代码执行完毕后在回到主线程
// 回到主线程
dispatch_async(group, dispatch_get_main_queue(), ^{
// 执行相关代码...
});
});
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
并发
队列,要是串行队列,这个函数就没啥意义了注意:
这个函数的第一个参数queue不能
是全局的并发队列-(void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12342234", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
// 在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执行
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}