靠谱的iOS程序员面试题我的理解

什么情况使用 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 显然这也是一个循环引用。这种情况就需要考虑循环引用的问题了.

你可能感兴趣的:(靠谱的iOS程序员面试题我的理解)