什么情况使用 weak 关键字,相比 assign 有什么不同?
什么时候使用weak:为防止循环引用时其中一端会用weak,如tableView的delegate.有对象的强引用时如:addSubView时,可以用weak.
weak相比assgin的不同:weak只能修饰oc对象类型,assgin还可以修饰基本数据类型,都是非持有关系,设置方法不释放旧值,不设置新值.weak修饰的对象释放时指针置为nil,assgin修饰的对象不置为nil,调用时可能会crash.
怎么用 copy 关键字?
NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
_userId = [userId copy]; //设置方法并不保留新值,而是将其“拷贝” (copy)
如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
需声明该类遵从 NSCopying 协议,如果是可变的对象需要实现NSMutableCopying,实现 NSCopying 协议。该协议只有一个方法:
- (id)copyWithZone:(NSZone*)zone;
如何重写带copy关键字的setter?
if(self = [super init]) {
_name = [name copy];}
@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter),存取方法有着严格的命名规范。 正因为有了这种严格的命名规范才能自动生成setter、getter方法.和带_的实例变量,也可以通过@systhesize来指定一个实例变量的名字.
@synthesize firstName = _myFirstName;
ivar、getter、setter是如何生成并添加到这个类中的?
@property 在编译过程中会生成5个对象1、OBJC_IVAR_$类名$属性名称(offset记录了存放变量距离对象内存地址的起始位置的偏移量)2、setter、getter函数3、ivar_list:成员变量列表 4、method_list:方法列表5、prop_list:属性列表.
每增加一个属性,系统就会往method_list添加setter和getter方法的描述、ivar_list会被添加变量的描述、prop_list会被添加属性的描述,然后根据offset去读取变量、方法的内存地址.
@protocol 和 category 中如何使用 @property
@protocol和category使用@property只会生成getter和setter方法,遵守协议的对象我们希望它能实现属性,如果一定要category增加属性的话可以借助运行时的两个函数:objc_setAssociatedObject
objc_getAssociatedObject
runtime 如何实现 weak 属性
weak修饰的属性并不需要去置为nil
在ARC环境无论是强指针还是弱指针都无需在 dealloc 设置为 nil , ARC 会自动帮我们处理。属性值所指的对象被置为nil时,属性值也会被置为nil.
objc_storeWeak(&a, b)理解为:objc_storeWeak(value, key),赋值对象(b)的内存地址作为键值key,将第一个参数--weak修饰的属性变量(a)的内存地址(&a)作为value.用weak修饰时a和b指向同一个内存地址,在b变nil时,a变nil。此时向a发送消息不会崩溃。而如果a是由 assign 修饰的,则: 在 b 非 nil 时,a 和 b 指向同一个内存地址,在 b 变 nil 时,a 还是指向该内存地址,变野指针。此时向 a 发送消息极易崩溃。
原理:weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
当目标对象销毁时,同时要发生的“事件”。借助 block 执行“事件”。
- (void)dealloc{ _block ?_block() :nil; }
调用block时将object置为nil
- (void)setObject:(NSObject *)object{
objc_setAssociatedObject(self,"object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{ _object =nil; }];
}
@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?
而编译器会把所有以init开头的方法当成初始化方法,而初始化方法只能返回 self 类型.编译器会报错
@property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString*initBy;
当你同时重写了 setter 和 getter 时,系统就不会生成 ivar(实例变量/成员变量)。
ARC下不显示指定属性的关键字,默认的关键字是什么?
基本数据类型:atomic、assgin、readwrite
OC对象类型:atomic、strong、readwrite
@synthesize和@dynamic分别有什么作用?
@property对应的就这两种@systhesize和@dynamic,默认的就是@systhesize var=_var,会自动生成getter、setter方法.
@dynamic 不会自动生成getter和setter方法,如:instance.var =someVar,在编译的时候不会报错但在运行的时候就会因为找不到setter方法而crash,someVar = var在运行时也会因为找不到getter方法而crash.这就是动态绑定的特点.
objc中向一个nil对象发送消息将会发生什么?
objc是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。
objc中再向一个对象发送消息时,runtime会根据对象的isa指针指向的类在类和它父类的方法列表中寻找方法运行,所有向一个nil对象发送消息时找到isa指针的地址为0时就直接返回了,所有不会发生错误.
[obj foo]和objc_msgSend()的关系?用clang把object-c编译成C++代码后,可以看到一下代码
((void()(id,SEL))(void)objc_msgSend)((id)obj,sel_registerName("foo"));
也就是说: [obj foo];在objc动态编译时,会被转意为:objc_msgSend(obj, @selector(foo));
runtime如何通过selector找到对应的IMP地址?
每一个类对象都对应着方法名称、方法实现、参数列表,selector就是方法名称,根据方法名称就可以在类对象的列表中找到方法的实现了.
使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?
1. 调用 -release :引用计数变为零,不能再有新的 __weak 弱引用, 否则将指向 nil.
2. 子类 调用 -dealloc,继承关系中每一层的父类 都在调用 -dealloc
3. NSObject 调 -dealloc,调用 Objective-C runtime 中的 object_dispose() 方法
4. 调用 object_dispose(),为 C++ 的实例变量们(iVars)调用 destructors,为 ARC 状态下的 实例变量们(iVars) 调用 -release,解除所有使用 runtime Associate方法关联的对象, 解除所有 __weak 引用,调用 free()
能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存已经确定.runtime 会调用class_setIvarLayout或class_setWeakIvarLayout来处理 strong weak 引用。所以不能向存在的类中添加实例变量;
运行时创建的类是可以添加实例变量,调用class_addIvar函数。但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前
runloop和线程有什么关系?runloop的几种mode?runloop怎样实现的?
每一个线程都有一个runloop,[NSRunLoop currentRunLoop]去获取当前线程的runloop.
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,
内存管理的机制?ARC怎么帮助开发者管理内存的?
每次runloop都会检查对象的retaincount,当对象的retainCount=0时,没有引用了.
MRC是在编译时添加retain/release/autorelease.ARC在编译期,用的是更底层的C接口实现的retain/release/autorelease,这也是为什么不用手动retain/release/autorelease的原因
Autorelease对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时释放。@autoreleasepool 在循环即将结束,会向自动释放池中的所有对象发送 release 消息,释放自动释放池中的所有对象。
autoreleasepool 以一个队列数组的形式实现,主要通过下列三个函数完成.objc_autoreleasepoolPush、objc_autoreleasepoolPop、objc_autorelease看函数名就可以知道,对 autorelease 分别执行 push,和 pop 操作。销毁对象时执行release操作。
BAD_ACCESS在什么情况下出现?如何调试BAD_ACCESS错误?
对一个已经释放的对象执行了release、访问已经释放对象的成员变量或者发消息.
1、objective-c的enable zombie objects
2、打全局断点迅速追踪
3、Xcode 7 已经集成了BAD_ACCESS捕获功能:Address Sanitizer
使用block发生引用循环,如何解决?
id weak weakSelf = self; 或者 weak __typeof(&*self)weakSelf = self该方法可以设置宏
id __block weakSelf = self;都可以防止循环引用,让一方强行置为nil
在block内如何修改block外部变量?
__block所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。栈区是红灯区,堆区才是绿灯区。
使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题,比如这些:
[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }];
__weak__typeof__(self) weakSelf = self;
_observer=[[NSNotificationCenter defaultCenter]addObserverForName:@"testKey" object:nil queue:nil usingBlock:^(NSNotification*note) {__typeof__(self) strongSelf = weakSelf; [strongSelf dismissModalViewControllerAnimated:YES]; }];
self --> _observer --> block --> self 显然这也是一个循环引用。这种情况就需要考虑循环引用的问题了.