iOS面试总结面试iOS面试题

iOS开发之高级面试题(三)

2019-03-06  本文已影响129人  0de1595b4560

小编加了一个不错的面试题和iOS技巧分享群,883872094群资料可以自取。分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。

iOS开发之高级面试题(一)

iOS开发之高级面试题(二)

iOS开发之高级面试题(四)

索引

    1. 21\.    @implementation Son : Father
          - (id)init
          {
              self = [super init];
              if (self) {
                  NSLog(@"%@", NSStringFromClass([self class]));
                  NSLog(@"%@", NSStringFromClass([super class]));
              }
              return self;
          }
          @end
      
      下面的代码输出什么?
      
  1. 22. runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

  2. 23. 使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

  3. 24. objc中的类方法和实例方法有什么本质区别和联系?

  4. 25. _objc_msgForward 函数是做什么的,直接调用它将会发生什么?

  5. 26. runtime如何实现weak变量的自动置nil?

  6. 27. 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

  7. 28. runloop和线程有什么关系?

  8. 29. runloop的mode作用是什么?

  9. 30. 以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

  10. 31. 猜想runloop内部是如何实现的?

  11. 32. objc使用什么机制管理对象内存?

  12. 33. ARC通过什么方式帮助开发者管理内存?

  13. 34. 不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)

  14. 35. BAD_ACCESS在什么情况下出现?

  15. 36. 苹果是如何实现autoreleasepool的?

  16. 37. 使用block时什么情况会发生引用循环,如何解决?

  17. 38. 在block内如何修改block外部变量?

  18. 39. 使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?

  19. 40. GCD的队列(dispatch_queue_t)分哪两种类型?

  20. 41. 如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

  21. 42. dispatch_barrier_async的作用是什么?

  22. 43. 苹果为什么要废弃dispatch_get_current_queue?

  23. 44. 以下代码运行结果如何?

```source-objc
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"2");
});
NSLog(@"3");
}
```
  1. 45. addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?

  2. 46. 如何手动触发一个value的KVO

  3. 47. 若一个类有实例变量 NSString *_foo ,调用setValue:forKey:时,可以以foo还是 _foo 作为key?

  4. 48. KVC的keyPath中的集合运算符如何使用?

  5. 49. KVC和KVO的keyPath一定是属性么?

  6. 50. 如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?

  7. 51. apple用什么方式实现对一个对象的KVO?

  8. 52. IBOutlet连出来的视图属性为什么可以被设置成weak?

  9. 53. IB中User Defined Runtime Attributes如何使用?

  10. 54. 如何调试BAD_ACCESS错误

  11. 55. lldb(gdb)常用的调试命令?

21. 下面的代码输出什么?

    @implementation Son : Father
    - (id)init
    {
        self = [super init];
        if (self) {
            NSLog(@"%@", NSStringFromClass([self class]));
            NSLog(@"%@", NSStringFromClass([super class]));
        }
        return self;
    }
    @end

答案:

都输出 Son

NSStringFromClass([self class]) = SonNSStringFromClass([super class]) = Son

这个题目主要是考察关于 Objective-C 中对 self 和 super 的理解。

我们都知道:self 是类的隐藏参数,指向当前调用方法的这个类的实例。那 super 呢?

很多人会想当然的认为“ super 和 self 类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者!他们两个的不同点在于:super 会告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。

当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。

这也就是为什么说“不推荐在 init 方法中使用点语法”,如果想访问实例变量 iVar 应该使用下划线( _iVar ),而非点语法( self.iVar )。

点语法( self.iVar )的坏处就是子类有可能覆写 setter 。假设 Person 有一个子类叫 ChenPerson,这个子类专门表示那些姓“陈”的人。该子类可能会覆写 lastName 属性所对应的设置方法:

//
//  ChenPerson.m
//  
//
//  Created by https://github.com/ChenYilong on 15/8/30.
//  Copyright (c) 2015年 http://weibo.com/luohanchenyilong/ 微博@iOS程序犭袁. All rights reserved.
//

#import "ChenPerson.h"

@implementation ChenPerson

@synthesize lastName = _lastName;

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, NSStringFromClass([self class]));
        NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, NSStringFromClass([super class]));
    }
    return self;
}

- (void)setLastName:(NSString*)lastName
{
    //设置方法一:如果setter采用是这种方式,就可能引起崩溃
//    if (![lastName isEqualToString:@"陈"])
//    {
//        [NSException raise:NSInvalidArgumentException format:@"姓不是陈"];
//    }
//    _lastName = lastName;

    //设置方法二:如果setter采用是这种方式,就可能引起崩溃
    _lastName = @"陈";
    NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, @"会调用这个方法,想一下为什么?");

}

@end

在基类 Person 的默认初始化方法中,可能会将姓氏设为空字符串。此时若使用点语法( self.lastName )也即 setter 设置方法,那么调用将会是子类的设置方法,如果在刚刚的 setter 代码中采用设置方法一,那么就会抛出异常,

为了方便采用打印的方式展示,究竟发生了什么,我们使用设置方法二。

如果基类的代码是这样的:

//
//  Person.m
//  nil对象调用点语法
//
//  Created by https://github.com/ChenYilong on 15/8/29.
//  Copyright (c) 2015年 http://weibo.com/luohanchenyilong/ 微博@iOS程序犭袁. All rights reserved.
//  

#import "Person.h"

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.lastName = @"";
        //NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, NSStringFromClass([self class]));
        //NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, self.lastName);
    }
    return self;
}

- (void)setLastName:(NSString*)lastName
{
    NSLog(@"类名与方法名:%s(在第%d行),描述:%@", __PRETTY_FUNCTION__, __LINE__, @"根本不会调用这个方法");
    _lastName = @"炎黄";
}

@end

那么打印结果将会是这样的:

 类名与方法名:-[ChenPerson setLastName:](在第36行),描述:会调用这个方法,想一下为什么?
 类名与方法名:-[ChenPerson init](在第19行),描述:ChenPerson
 类名与方法名:-[ChenPerson init](在第20行),描述:ChenPerson

我在仓库里也给出了一个相应的 Demo(名字叫:Demo_21题_下面的代码输出什么)。有兴趣可以跑起来看一下,主要看下他是怎么打印的,思考下为什么这么打印。

接下来让我们利用 runtime 的相关知识来验证一下 super 关键字的本质,使用clang重写命令:

    $ clang -rewrite-objc test.m

将这道题目中给出的代码被转化为:

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){ (id)self, (id)class_getSuperclass(objc_getClass("Son")) }, sel_registerName("class"))));

从上面的代码中,我们可以发现在调用 [self class] 时,会转化成 objc_msgSend函数。看下函数定义:

    id objc_msgSend(id self, SEL op, ...)

我们把 self 做为第一个参数传递进去。

而在调用 [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;
};

结构体有两个成员,第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self 。第二个成员是记录当前类的父类是什么。

所以,当调用 [self class] 时,实际先调用的是 objc_msgSend函数,第一个参数是 Son当前的这个实例,然后在 Son 这个类里面去找 - (Class)class这个方法,没有,去父类 Father里找,也没有,最后在 NSObject类中发现这个方法。而 - (Class)class的实现就是返回self的类别,故上述输出结果为 Son。

objc Runtime开源代码对- (Class)class方法的实现:

- (Class)class {
    return object_getClass(self);
}

而当调用 [super class]时,会转换成objc_msgSendSuper函数。第一步先构造 objc_super 结构体,结构体第一个成员就是 self 。 第二个成员是 (id)class_getSuperclass(objc_getClass(“Son”)) , 实际该函数输出结果为 Father。

第二步是去 Father这个类里去找 - (Class)class,没有,然后去NSObject类去找,找到了。最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用,

此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son。

参考链接:微博@Chun_iOS的博文刨根问底Objective-C Runtime(1)- Self & Super

22. runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

每一个类对象中都一个方法列表,方法列表中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.

23. 使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

无论在MRC下还是ARC下均不需要。

2011年版本的Apple API 官方文档 - Associative References 一节中有一个MRC环境下的例子:

// 在MRC下,使用runtime Associate方法关联的对象,不需要在主对象dealloc的时候释放
// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)
// https://github.com/ChenYilong
// 摘自2011年版本的Apple API 官方文档 - Associative References 

static char overviewKey;

NSArray *array =
    [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];
// For the purposes of illustration, use initWithFormat: to ensure
// the string can be deallocated
NSString *overview =
    [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];

objc_setAssociatedObject (
    array,
    &overviewKey,
    overview,
    OBJC_ASSOCIATION_RETAIN
);

[overview release];
// (1) overview valid
[array release];
// (2) overview invalid

文档指出

At point 1, the string overview is still valid because the OBJC_ASSOCIATION_RETAIN policy specifies that the array retains the associated object. When the array is deallocated, however (at point 2), overview is released and so in this case also deallocated.

我们可以看到,在[array release];之后,overview就会被release释放掉了。

既然会被销毁,那么具体在什么时间点?

根据 WWDC 2011, Session 322 (第36分22秒) 中发布的内存销毁时间表,被关联的对象在生命周期内要比对象本身释放的晚很多。它们会在被 NSObject -dealloc 调用的 object_dispose() 方法中释放。

对象的内存销毁时间表,分四个步骤:

// 对象的内存销毁时间表// http://weibo.com/luohanchenyilong/ (微博@iOS程序犭袁)// https://github.com/ChenYilong// 根据 WWDC 2011, Session 322 (36分22秒)中发布的内存销毁时间表   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()

对象的内存销毁时间表:参考链接

24. objc中的类方法和实例方法有什么本质区别和联系?

类方法:

  1. 类方法是属于类对象的
  2. 类方法只能通过类对象调用
  3. 类方法中的self是类对象
  4. 类方法可以调用其他的类方法
  5. 类方法中不能访问成员变量
  6. 类方法中不能直接调用对象方法

实例方法:

  1. 实例方法是属于实例对象的
  2. 实例方法只能通过实例对象调用
  3. 实例方法中的self是实例对象
  4. 实例方法中可以访问成员变量
  5. 实例方法中直接调用实例方法
  6. 实例方法中也可以调用类方法(通过类名)

25
_objc_msgForward
函数是做什么的,直接调用它将会发生什么?

_objc_msgForward
是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,
_objc_msgForward
会尝试做消息转发。

我们可以这样创建一个
_objc_msgForward

对象:

IMP msgForwardIMP = _objc_msgForward;

在上篇中的《objc中向一个对象发送消息
[obj foo]

objc_msgSend()
函数之间有什么关系?》曾提到
objc_msgSend
在“消息传递”中的作用。在“消息传递”过程中,
objc_msgSend
的动作比较清晰:首先在 Class 中的缓存查找 IMP (没缓存则初始化缓存),如果没找到,则向父类的 Class 查找。如果一直查找到根类仍旧没有实现,则用
_objc_msgForward
函数指针代替 IMP 。最后,执行这个 IMP 。
Objective-C运行时是开源的,所以我们可以看到它的实现。打开 Apple Open Source 里Mac代码里的obj包下载一个最新版本,找到
objc-runtime-new.mm,
进入之后搜索
_objc_msgForward

image.png

里面有对
_objc_msgForward
的功能解释:

image.png

/***********************************************************************


objc-runtime-new.mm
文件里与
_objc_msgForward
有关的三个函数使用伪代码展示下:

// objc-runtime-new.mm 文件里与 _objc_msgForward 有关的三个函数使用伪代码展示
// Created by https://github.com/ChenYilong
// Copyright (c) 微博@iOS程序犭袁(http://weibo.com/luohanchenyilong/). All rights reserved.
// 同时,这也是 obj_msgSend 的实现过程

id objc_msgSend(id self, SEL op, ...) {
    if (!self) return nil;
    IMP imp = class_getMethodImplementation(self->isa, SEL op);
    imp(self, op, ...); //调用这个函数,伪代码...
}


//查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
    if (!cls || !sel) return nil;
    IMP imp = lookUpImpOrNil(cls, sel);
    if (!imp) return _objc_msgForward; //_objc_msgForward 用于消息转发
    return imp;
}

IMP lookUpImpOrNil(Class cls, SEL sel) {
    if (!cls->initialize()) {
        _class_initialize(cls);
    }

    Class curClass = cls;
    IMP imp = nil;
    do { //先查缓存,缓存没有时重建,仍旧没有则向父类查询
        if (!curClass) break;
        if (!curClass->cache) fill_cache(cls, curClass);
        imp = cache_getImp(curClass, sel);
        if (imp) break;
    } while (curClass = curClass->superclass);

    return imp;
}

虽然Apple没有公开
_objc_msgForward
的实现源码,但是我们还是能得出结论:

_objc_msgForward
是一个函数指针(和 IMP 的类型一样),是用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,
_objc_msgForward
会尝试做消息转发。
在上篇中的《objc中向一个对象发送消息
[obj foo]

objc_msgSend()
函数之间有什么关系?》曾提到
objc_msgSend
在“消息传递”中的作用。在“消息传递”过程中,
objc_msgSend
的动作比较清晰:首先在 Class 中的缓存查找 IMP (没缓存则初始化缓存),如果没找到,则向父类的 Class 查找。如果一直查找到根类仍旧没有实现,则用
_objc_msgForward
函数指针代替 IMP 。最后,执行这个 IMP 。

为了展示消息转发的具体动作,这里尝试向一个对象发送一条错误的消息,并查看一下
_objc_msgForward
是如何进行转发的。
首先开启调试模式、打印出所有运行时发送的消息: 可以在代码里执行下面的方法:
(void)instrumentObjcMessageSends(YES);
或者断点暂停程序运行,并在 gdb 中输入下面的命令:
call (void)instrumentObjcMessageSends(YES)
以第二种为例,操作如下所示:之后,

image.png

运行时发送的所有消息都会打印到
/tmp/msgSend-xxxx
文件里了。
终端中输入命令前往:
open /private/tmp

image.png

可能看到有多条,找到最新生成的,
双击打开在模拟器上执行执行以下语句(这一套调试方案仅适用于模拟器,真机不可用,关于该调试方案的拓展链接:Can the messages sent to an object in Objective-C be monitored or printed out? ),向一个对象发送一条错误的消息://

// main.m
// CYLObjcMsgForwardTest
//
// Created by http://weibo.com/luohanchenyilong/.
// Copyright (c) 2015年 微博@iOS程序犭袁. All rights reserved.
//

import <UIKit/UIKit.h>

import "AppDelegate.h"

import "CYLTest.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        CYLTest *test = [[CYLTest alloc] init];
        [test performSelector:(@selector(iOS程序犭袁))];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
image.png

你可以在
/tmp/msgSend-xxxx
(我这一次是
/tmp/msgSend-9805
)文件里,看到打印出来:

image.png

1.resolveInstanceMethod:
方法 (或
resolveClassMethod:
)。
2.forwardingTargetForSelector:
方法
3.methodSignatureForSelector:
方法
4.forwardInvocation:
方法
5.doesNotRecognizeSelector:`
方法

为了能更清晰地理解这些方法的作用,git仓库里也给出了一个Demo,名称叫“
_objc_msgForward_demo
”,可运行起来看看。
下面回答下第二个问题“直接
_objc_msgForward
调用它将会发生什么?”

直接调用
_objc_msgForward
是非常危险的事,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事。
就好像跑酷,干得好,叫“耍酷”,干不好就叫“作死”。
正如前文所说:

_objc_msgForward
是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,
_objc_msgForward
会尝试做消息转发。

如何调用
_objc_msgForward

_objc_msgForward
隶属 C 语言,有三个参数 :

-- objc_msgForward 类型
1 所属对象 id类型
2 方法名 SEL类型
3 可变参数 可变参数类型

首先了解下如何调用 IMP 类型的方法,IMP类型是如下格式:
为了直观,我们可以通过如下方式定义一个 IMP类型 :
typedef void (*voidIMP)(id, SEL, ...)
一旦调用
_objc_msgForward
,将跳过查找 IMP 的过程,直接触发“消息转发”,如果调用了
_objc_msgForward
,即使这个对象确实已经实现了这个方法,你也会告诉
objc_msgSend

“我没有在这个对象里找到这个方法的实现”想象下

objc_msgSend
会怎么做?通常情况下,下面这张图就是你正常走
objc_msgSend
过程,和直接调用
_objc_msgForward
的前后差别:

有哪些场景需要直接调用
_objc_msgForward
?最常见的场景是:你想获取某方法所对应的
NSInvocation
对象。举例说明:
JSPatch (Github 链接)就是直接调用_objc_msgForward来实现其核心功能的:

JSPatch 以小巧的体积做到了让JS调用/替换任意OC方法,让iOS APP具备热更新的能力。

作者的博文《JSPatch实现原理详解》详细记录了实现原理,有兴趣可以看下。

同时 RAC(ReactiveCocoa) 源码中也用到了该方法。

26. runtime如何实现weak变量的自动置nil?

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

__weak
引用的解除时间。)我们可以设计一个函数(伪代码)来表示上述机制:objc_storeWeak(&a, b)
函数:
objc_storeWeak
函数把第二个参数--赋值对象(b)的内存地址作为键值key,将第一个参数--weak修饰的属性变量(a)的内存地址(&a)作为value,注册到 weak 表中。如果第二个参数(b)为0(nil),那么把变量(a)的内存地址(&a)从weak表中删除,你可以把
objc_storeWeak(&a, b)
理解为:
objc_storeWeak(value, key)
,并且当key变nil,将value置nil。
在b非nil时,a和b指向同一个内存地址,在b变nil时,a变nil。此时向a发送消息不会崩溃:在Objective-C中向nil发送消息是安全的。
而如果a是由assign修饰的,则: 在b非nil时,a和b指向同一个内存地址,在b变nil时,a还是指向该内存地址,变野指针。此时向a发送消息极易崩溃。
下面我们将基于

objc_storeWeak(&a, b)
函数,使用伪代码模拟“runtime如何实现weak属性”:
// 使用伪代码模拟:runtime如何实现weak属性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong

 id obj1;
 objc_initWeak(&obj1, obj);
/*obj引用计数变为0,变量作用域结束*/
 objc_destroyWeak(&obj1);

下面对用到的两个方法
objc_initWeak

objc_destroyWeak
做下解释:总体说来,作用是: 通过
objc_initWeak
函数初始化“附有weak修饰符的变量(obj1)”,在变量作用域结束时通过objc_destoryWeak
函数释放该变量(obj1)。下面分别介绍下方法的内部实现:objc_initWeak
函数的实现是这样的:在将“附有weak修饰符的变量(obj1)”初始化为0(nil)后,会将“赋值对象”(obj)作为参数,调用
objc_storeWeak函数。

obj1 = 0;
obj_storeWeak(&obj1, obj);

也就是说:weak 修饰的指针默认值是 nil (在Objective-C中向nil发送消息是安全的)

然后
obj_destroyWeak
函数将0(nil)作为参数,调用
objc_storeWeak函数。
objc_storeWeak(&obj1, 0);
前面的源代码与下列源代码相同。

// 使用伪代码模拟:runtime如何实现weak属性
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
/* ... obj的引用计数变为0,被置nil ... */
objc_storeWeak(&obj1, 0);

objc_storeWeak
函数把第二个参数--赋值对象(obj)的内存地址作为键值,将第一个参数--weak修饰的属性变量(obj1)的内存地址注册到 weak 表中。如果第二个参数(obj)为0(nil),那么把变量(obj1)的地址从weak表中删除。

27. 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

[iOS开发之高级面试题(一)]( https://www.jianshu.com/p/78f66f1d633e)

[iOS开发之高级面试题(二)](https://www.jianshu.com/p/353f93bf4912)

[iOS开发之高级面试题(四) ](https://www.jianshu.com/p/ebcac56221e9)

>小编加了一个不错的面试题和iOS技巧分享群,883872094群资料可以自取。分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。
>[原文地址](https://www.jianshu.com/p/c71397fbcebf)
图文来源于网络,如有侵权请联系小编删除
上一篇 下一篇

猜你喜欢

热点阅读