[※]@property中有哪些属性关键字?
原子属性
atomic 和 nonatomic用来决定编译器生成的getter和setter是否为原子操作,atomic 设置成员变量的@property属性时 默认为是atomic 提供线程安全。
nonatomic 非原子性访问对于属性赋值的时候不加锁,多线程并发访问会提高性能,如果不加此属性则默认是两个访问方法都为原子型事务访问。
读写属性
readonly 此标记说明属性是只读的
readwrite 此标记说明属性会被当成读写的 这也是默认的属性
内存管理语义
assign、strong、 weak、unsafe_unretained、copy
ARC 默认属性
atomic、readwrite、strong(对象)/assign(基本数据)
[※]weak属性需要在dealloc中置nil么?
不需要。
在ARC环境无论是强指针还是弱指针都无需在 dealloc 设置为 nil , ARC 会自动帮我们处理
即便是编译器不帮我们做这些,weak也不需要在 dealloc 中置nil:
[※※※]@synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?
- 如果指定了成员变量的名称,会生成一个指定的名称的成员变量,
- 如果这个成员已经存在了就不再生成了.
- 如果是 @synthesize foo;
还会生成一个名称为foo的成员变量,也就是说:
如果没有指定成员变量的名称会自动生成一个属性同名的成员变量, - 如果是 @synthesize foo = _foo;
就不会生成成员变量了.
[※※※※※]在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?
- 同时重写了setter和getter函数
- 重写了只读属性property的getter(这里property必须声明在.m文件中)
- 想要自定义实例变量的变量名
- 声明在@protocol中的property
- 重载的属性
备注:
重载属性时最好使用@dynamic,虽然@synthesize也可以用
[※※]objc中向一个nil对象发送消息将会发生什么?
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
objc在向一个对象发送消息时,
1.runtime库会根据对象的isa指针找到该对象实际所属的类,
2.然后在该类中的方法列表以及其父类方法列表中寻找方法运行,
3.然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。那么,回到本题,如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。
如果给一个已经释放的对象发送消息,会崩溃。
objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?
[obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj, @selector(foo));
[※※※]什么时候会报unrecognized selector的异常?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,如果,在最顶层的父类中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX 。
objc的运行时会给出三次拯救程序崩溃的机会
Method resolution、Fast forwarding、Normal forwarding
[※※※※]一个objc对象的isa的指针指向什么?有什么作用?
[※※※※]下面的代码输出什么?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
简单来说,self和super都是指向当前实例的,不同的是,[self class]会在当前类的方法列表中去找class这个方法,[super class]会直接开始在当前类的父类中去找calss这个方法,两者在找不到的时候,都会继续向祖先类查询class方法,最终到NSObject类。那么问题来了,由于我们在Father和Son中都没有去重写class这个方法,最终自然都会去执行NSObject中的class方法,结果也自然应该是一样的。至于为什么是Son,我们可以看看NSObject中class的实现:
-(Class)class {
return object_getClass(self);
}
这就说的通了,返回的都是self的类型,self此处正好就是Son,因此结果就会输出Son。
[※※※※]runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
在寻找IMP的地址时,runtime提供了两种方法
IMP class_getMethodImplementation(Class cls, SEL name); // 效率更高
IMP method_getImplementation(Method m)
1、IMP class_getMethodImplementation(Class cls, SEL name); // 效率更高
类方法(假设有一个类A)
class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));
实例方法
class_getMethodImplementation([A class],@selector(methodName));
2、IMP method_getImplementation(Method m)
类方法
Method class_getClassMethod(Class cls, SEL name)
实例方法
Method class_getInstanceMethod(Class cls, SEL name)
[※※※※]使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?
- (void)setAddProperty:(id)addProperty {
objc_setAssociatedObject(self, @selector(addProperty), addProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)addProperty {
return objc_getAssociatedObject(self, @selector(addProperty));
}
- (void)dealloc {
[self setAddProperty:nil];
}
不需要,dealloc的时候会调用object_dispose,这儿会处理关联对象,weak对象
[※※※※※]:objc中的类方法和实例方法有什么本质区别和联系?
实例方法中可以使用类方法,用类名去调用
比如有一个类 example
里面定义 了两个方法
+(void)fun1; //类方法
-(void)fun2; //实例方法
用法:
[example fun1];
example *tmp = [[example alloc]init];
[tmp fun2];
问题[※※※※※]:_objc_msgForward函数是做什么的,直接调用它将会发生什么?
直接调用_objc_msgForward是非常危险的事,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。
一旦调用_objc_msgForward,将跳过查找 IMP 的过程,直接触发“消息转发”,如果调用了_objc_msgForward,即使这个对象确实已经实现了这个方法,你也会告诉objc_msgSend:“我没有在这个对象里找到这个方法的实现”
runtime如何实现weak变量的自动置nil?
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)
[※※※※※]:能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
不能向编译后得到的类中增加实例变量;
能向运行时创建的类中添加实例变量;
原因如下:
因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;
运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
Runloop和线程的关系
runloop与线程是一一对应的,一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。
runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
runloop在第一次获取时被创建,在线程结束时被销毁。
对于主线程来说,runloop在程序一启动就默认创建好了。
对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调。
//猜想runloop内部是如何实现的?
1、有一个判断循环的条件,满足条件,就一直循环
2、线程得到唤醒事件被唤醒,事件处理完毕以后,回到睡眠状态,等待下次唤醒