autoreleasePool
autoreleasePool认识
autoreleasePool怎么加进去的,什么时候push,什么时候pop,什么时候会用
Autoreleasepool的原理?所使用的的数据结构是什么
- 自动释放池,当由autorelease修饰或者@autoreleasePool代码中的临时变量会被加到自动是释放池中,当离开@autoreleasePool代码或者,RunLoop即将进入休眠时会对所有放入自动的释放池的对象发送release消息。
- autoreleasePool是由autoreleasePoolPage组成的双向链表实现的,autoreleasePoolPage中会有指向上个节点和下个节点的执行,还有当前page栈顶的地址,自动释放池是可以嵌套的,在单个释放池开始的时候会执行push操作,插入一个哨兵对象,标记单个自动释放池的开始地址,当这个自动释放池要结束的时候会进行pop操作,对当前地址到哨兵对象之间的对象发送release消息,并将其从page中删除。
- push操作时机: 使用@autoreleasePool代码块,刚进入RunLoop时,RunLoop即将唤醒时。pop:离开@autoreleasePool代码块,RunLoop即将进入休眠时。
- 每一个线程创建的时候就会有一个autorelease pool的创建,并且在线程退出的时候,清空整个autorelease pool。
关联对象
如何给关联对象设置成弱引用
- (void)setContext:(CDDContext*)object {
id __weak weakObject = object;
id (^block)() = ^{ return weakObject; };
objc_setAssociatedObject(self, @selector(context), block, OBJC_ASSOCIATION_COPY);
}
- (CDDContext*)context {
id (^block)() = objc_getAssociatedObject(self, @selector(context));
id curContext = (block ? block() : nil);
return curContext;
}
关联对象的应用?系统如何实现关联对象的
关联对象的如何进行内存管理的?
objc_associatedObject(observer, key, value, police)
AssociationsManager
AssociationsHashMap <observer, ObjcAssociationsMap>
ObjcAssociationsMap <key ObjcAssociation>
ObjcAssociation: value police
set:根据police去存,如果是retain则进行root_retain操作,如果是copy则进行copy操作, 将isa_t has_assoc设置YES
weak
weak unowned的区别
todo:
weak 原理,一个对象如果有多个weak指针指向,怎么销毁
对象释放时,调用clearDeallocating函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个enrty从weak表中删除,然后清除对象的记录。
weak的实现原理?SideTable的结构是什么样的
struct SideTables {
}
struct SideTable {
spinlock_t lock;
RefcountMap refcnts;
weak_table_t weak_table;
}
struct weak_table_t {
// 存储弱引用对象的相关信息
weak_entry_t *weak_entries;
// 数组中的个数
size_t num_entries;
uintptr_t mask;
// 可能会发生的hash冲突的最大次数
uintptr_t max_hash_displacement;
}
struct weak_entry_t {
// 被弱引用的对象
DisguisedPtr<objc_object> referent;
union{
struct {
// 弱引用该对象指针地址的hash数组
weak_referrer_t *referents;
// 是否使用动态hash数组标记位
uintptr_t out_of_line_ness: 2
uintptr_t num_refs;
uintptr_t mask;
// 可能会发生的hash冲突的最大次数
uintptr_t max_hash_displacement;
}
struct {
// 弱引用该对象指针地址的带下固定的hash数组4个
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
}
}
}
当弱引用该对象的指针数目小于等于WEAK_INLINE_COUNT则使用inline_referrers否则使用out_of_line_ness。
weak 与 assign 得区别?代理使用assign会出现问题吗?
assign一般用于值引用类型,如果会代理使用assign,会造成野指针的问题。
野指针:当对象被释放或者收回,但是对该指针么有任何的修改,以至于该指针仍旧指向已经回收的内存地址,这将会产生无法预料的结果。assign可以修饰值引用类型是因为其存的是值而不是指针。
其它内存
内存泄漏的几种方法/ARC下哪些情况会造成内存泄漏
block delegate NSTimer CF需要自己释放 通知(iOS9之后苹果修复) 相互引用
block一定会造成强引用吗
不一定,还是要根据是否产生了循环引用来分析。
循环引用,为什么要在block中加strong,不加会怎样
delloc的调用顺序
ARC的实现原理?ARC下对retain & release做了哪些优化
Category
为什么Category 不能添加属性,为什么还要设计ro,直接添加不是很方便么,如果将flag改变ro能不能写入,改变了会怎样
运行时对象的内存布局早已经确定了,如果此时添加成员变量会破坏内存布局。
在编译期间确定对象的内存布局,并且将对象的方法和协议收集记录起来,
Category 和主类方法谁先调用
只会调用category里的方法,category的调用顺序根据文件的编译顺序compile sources(只会调用最后参加编译的分类)
+load:load的执行顺序是先类后分类,分类的顺序是按照参加编译的顺序来的
category & extension区别,能给NSObject添加Extension吗,结果如何
extension在编译期决议,他就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,他伴随类的产生而产生,extension可以添加实例变量。不能给NSObject添加Extension,因为在extension中添加的方法或属性必须在源类的文件的.m文件中实现才可以。
分类的作用?底层是怎样得?
作用:
- 将私有方法公开
- 将类的实现分开在几个不同的文件里。
底层:category加载过程:
- 编译器生成了实例方法列表和属性列表。
- 编译器在DATA段下的objc_catlist section里保存了一个的category_t数组。
- 在load_images时吧category的实例方法,协议以及属性添加到类上
typedef struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethod;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
}
消息转发:
methodswizzling的应用,方法交换得底层?
方法交换:
Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(swizzleClass ?: theClass, swizzledSelector);
// 先向theClass中添加原方法,imp指针指向新方法
BOOL success = class_addMethod(theClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (success) {
// 如果成功则将theClass中的swizzledSelector方法的imp替换为老方法的imp
class_replaceMethod(theClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 如果失败则表明原方法之前就存在,则直接交互
method_exchangeImplementations(originalMethod, swizzledMethod);
}
做这个处理是为了防止修改的是父类里的方法,这样的话会影响到他的兄弟类
NSObject缺少方法
- (id)wbSafeKit_forwardingTargetForSelector:(SEL)selector {
BOOL aBool = [self respondsToSelector:@selector(selector)];
if (aBool || signature) {
return [self wbSafeKit_forwardingTargetForSelector:selector];
} else {
safeKit_reportBug...
// 如果调用了没有实现的方法,则给当前类添加这个方法,之后再继续执行
[safeKitObj addAnyFunc:selector];
return safeKitObj;
}
}
- (void)addAnyFunc:(SEL)selector {
funcTypeEncodeing:有多少参数则需要拼多少@
class_addMethod([WSafeKitObj class], selector, (IMP)safeKitGodIMP, funcTypeEncodeing);
}
NSString/NSNumber
判断是否为对方,如果为对方则直接创建对方
NSArray/NSMutableArray
- objectAtIndex;
- addObject;
- insert/remove/replace/set..index
NSDictory(__NSPlaceholderDictionary)/NSMutableDictory(__NSDictoryM)
- setObject:forKey object/key/try
- removeObjectForKey key
埋了什么点,如果给按钮点击埋点怎么实现,为什么交换方法要自己调用自己不会递归么
不会递归,因为交换方法调用方法的imp指针指向的不是自己。
SEL怎么找到imp的,消息发送流程
实例对象的isa指向的是类对象,首先会到cache中去找Method,如果找不到则去MethodList中去找,如果找不到则通过superClass从其父类中去找,直到NSObject的类对象。如果找到了则获取Method中的imp指针。
类的isa指针指向的是元类对象。
消息转发流程,引出错误捕捉
OC的消息转发机制:
怎么保证自己的类一定能调用到自己写的方法?(不知道有没有人会写一个分类,里面有和自己的类方法名字一模一样)(美团)
Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
在方法调用的时候,方法查询-> 动态解析-> 消息转发 之前做了什么
说说消息转发机制的优劣(和其它语言相比)
中内省的几个方法有哪些?内部实现原理是什么
内省
isSubclassOfClass:是否是另一个类型的子类
isAncestorOfObject:是否是另一个类型非父类
respondsToSelector:是否能够相应某个方法
conformsToProtocol:是否遵循某个协议
class方法和object_getClass方法有什么区别
- 实例class方法就直接返回object_getClass(self)
- 类class方法直接返回self,而object_getClass
isKindOfClass&isMemberOfClass
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[AKPerson class] isKindOfClass:[AKPerson class]];
BOOL re4 = [(id)[AKPerson class] isMemberOfClass:[AKPerson class]];
NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n", re1, re2, re3, re4);
// 題目2
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[AKPerson alloc] isKindOfClass:[AKPerson class]];
BOOL re8 = [(id)[AKPerson alloc] isMemberOfClass:[AKPerson class]];
NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n", re5, re6, re7, re8);
}
return 0;
}
+ (BOOL)isKindOfClass:(Class)cls {
// 获取类对象的isa指针(此时指向的是元类)
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
// 如果元类/元类的父类/NSObject类对象 == cls则返回YES
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
// 获取实例对象的isa指针(此时指向的是类对象)
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
// 如果类对象/类对象的父类 == cls则返回YES
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
// 获取类对象的isa指针(此时指向的是元类)
// 如果元类 == cls则返回YES
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
// 获取实例对象的isa指针(此时指向的是类对象)
// 如果类对象 == cls则返回YES
return [self class] == cls;
}
Class object_getClass(id obj) {
if (obj) return obj->getIsa();
else return Nil;
}
- (Class)class {
return object_getClass(self);
}
通知
实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等)
typedef struct Obs {
id observer;
SEL selector;
struct Obs *next; 指向的下一个节点
int retained; 结构保留引用计数
struct NCTbl *link;
}
typedef struct NCTbl {
// 存储没有传入名字的通知
GSIMapTable nameless;
// 存储传入名字的通知
GSIMapTable named;
Observation *wildcard;
NSRecursiveLock *_lock
}
nameless表中:
GSIMapTable 的结构如下
object:Observation
object:Observation
object:Observation
named表中:
GSIMapTable 的结构如下
name : maptable
name : maptable
name : maptable
maptable的结构如下
object : Observation
object : Observation
object : Observation
NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
由于TABLE资源的问题同一个现场会安顺序执行
postNoticationName:
lockNCTable(TABLE);
...//找到对应的observer对象
unlockNCTable(TABLE);
...//执行performSelector
NSNotificationQueue是异步还是同步发送?在哪个线程响应
NSNotificationQueue和runloop的关系
NSNotificationQueue是依赖runLoop的,
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
// 当runloop处于空闲状态时post
NSPostWhenIdle = 1,
// 当当前runloop完成之后立即post
NSPostASAP = 2,
// 立即post
NSPostNow= 3
};
如何保证通知接收的线程在主线程
- 在主线程post
- 接收到通知后切主线程
- 使用blcok接口addObserverForName:queue... 指定queue为主队列
页面销毁时不移除通知会崩溃吗
之前崩溃是因为持有Observer的是__unsafe_unretain指针对象,当对象释放时会有野指针的问题
现在使用的是weak所以不会有问题
多次添加同一个通知会是什么结果?多次移除通知呢
源码没有进行重复过滤,所以添加一同一个通知,等于就是添加了两次,post会触发两次
下面的方式能接收到通知吗?为什么
NSObject *object = [[NSObject alloc] init];
struct objc_object {
isa_t isa;
}
struct objc_class : objc_object {
isa_t isa;
Class superclass;
cache_t cache;
class_data_bits_t bits;
}
union isa_t {
isa_t() {}
isa_t(uintptr_t value) : bits(value) {}
Class cls;
unitpr_t bits;
struct {
uintptr_t indexed:1
uintptr_t has_assoc:1
uintptr_t has_cxx_dtor:1
uintptr_t shiftcls:44
uintptr_t magic:6
uintptr_t weakly_referenced:1
uintptr_t deallocating:1
uintptr_t has_sidetable_tc:1
uintptr_t extra_tc:8
}
}
indexed:
0 表示raw isa,也就是没有结构体的部分,访问对象的isa会直接返回一个cls的指针。
1 表示isa不是指针,关于类的指针都是保存在shiftcls中。
has_assoc:
对象含有或者曾经含有关联引用,没有关联引用的可以更快的释放。
weakly_referenced:
对象被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。
deallocating:
对象正在释放内存
has_sidetable_rc:
对象的引用计数太大了,存不下
extra_rc:
对象的引用计数超过1,会存在这里,如果引用计数为10,extra_rc的值就为9
class_data_bits_t *data;
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
uint32_t reserved;
const uint8_t * ivarLayout;
const char *name;
method_list_t *baseMethodList;
protocol_list_t *baseProtocols;
const ivar_list_t *ivars;
const uint8_t *weakIvarLayout;
property_list_t *baseProperties;
}
在编译期间类的结构中的class_data_bits_t *data指向的是一个class_ro_t *指针,然后再加在ObjC运行时的过程中再realizeClass方法中:
1. 从class_data_bits_t调用data方法,结果从class_rw_t强制转换为class_ro_t指针。
2. 初始化一个class_rw_t结构体。
3. 设置结构体ro的值以及flag。
4. 最后设置正确的data。
之后realizeClass调用methodizeClass方法来将类自己实现的方法,属性和遵循的协议加载到methods,properties和protocols列表中
类再内存的位置是在编译期就决定的,
在OC中,对象的方法并没有存储在对象的结构体中,当实例方法被调用时,他要通过自己持有的isa来查找对应的类,然后在这里的class_data_bits_t结构体中查找对应方法的实现,如果找不到对应的方法实现,通过super_class从用父类去查询,直到根类。
SideTable
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
方法的本质是什么?SEL/IMP分别是什么?二者有什么联系?
-
方法的本质是发送消息:
- 快速查找:objc_msgSend~cache_t.
- 慢速查找:lookUpImpOrForward 递归自己和自己的父类
- 查不到消息:resolveInstanceMethod 动态方法解析
- 消息快速转发:forwardingTargetForSelector
- 慢速转发:
methodSignatureForSelector
&forwardInvocation
-
SEL/IMP分别是什么?二者有什么联系?
- SEL是方法编号,在read_images期间就编译进了内存。
- IMP是函数实现指针,找IMP就是找函数的过程。
struct objc_method {
SEL method_name;
Char *method_types;
IMP method_imp;
}
method_name 类型为SEL:
selector 方法选择器,也是方法的编号,是类成员方法的指针,无论什么类里,只要方法名相同,SEL实际时根据方法名hash化了的字符串。而对于字符串的比较仅仅需要比较他们的地址就可以了,SEL的存在加快了查询方法的速度。
method_types 是一个char指针,存储着方法的参数类型和返回值类型
method_imp 方法实现,类型为IMP
在类的(实例和类方法)调度表(dispatch table)中的每一个实例代表一个方法Method,其名叫做选择器SEL,并对应着一种方法实现称之为IMP,有了Method就可以使用SEL找到对应的IMP,SEL就是为了查找方法的最终实现IMP。
内省
isSubclassOfClass:是否是另一个类型的子类
isAncestorOfObject:是否是另一个类型非父类
respondsToSelector:是否能够相应某个方法
conformsToProtocol:是否遵循某个协议
class方法和object_getClass方法有什么区别
- 实例class方法就直接返回object_getClass(self)
- 类class方法直接返回self,而object_getClass
isKindOfClass&isMemberOfClass
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[AKPerson class] isKindOfClass:[AKPerson class]];
BOOL re4 = [(id)[AKPerson class] isMemberOfClass:[AKPerson class]];
NSLog(@"\n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n", re1, re2, re3, re4);
// 題目2
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[AKPerson alloc] isKindOfClass:[AKPerson class]];
BOOL re8 = [(id)[AKPerson alloc] isMemberOfClass:[AKPerson class]];
NSLog(@"\n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n", re5, re6, re7, re8);
}
return 0;
}
+ (BOOL)isKindOfClass:(Class)cls {
// 获取类对象的isa指针(此时指向的是元类)
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
// 如果元类/元类的父类/NSObject类对象 == cls则返回YES
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
// 获取实例对象的isa指针(此时指向的是类对象)
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
// 如果类对象/类对象的父类 == cls则返回YES
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
// 获取类对象的isa指针(此时指向的是元类)
// 如果元类 == cls则返回YES
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
// 获取实例对象的isa指针(此时指向的是类对象)
// 如果类对象 == cls则返回YES
return [self class] == cls;
}
Class object_getClass(id obj) {
if (obj) return obj->getIsa();
else return Nil;
}
- (Class)class {
return object_getClass(self);
}
[self class] & [super class]
AKStudent
是AKPerson
的子類, 主程序初始化AKStudent
會打印什麼?
#import "AKStudent.h" @implementation AKStudent
- (instancetype)init {
if (self = [super init]) {
NSLog(@"[self class] = %@", NSStringFromClass([self class]));
NSLog(@"[super class] = %@", NSStringFromClass([super class]));
}
return self;
}
@end
结果:
[self class] = AKStudent
[super class] = AKStudent
[super class] -> objc_msgSendSuper
objc_msgSendSuper(
void /* struct objc_super *super, SEL op, ... */ )
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
};
)
objc_msgSend走的是消息查找流程,会递归查找class方法;objc_msgSendSuper直接跳过self的查找,从objc_super结构体开始。
Class与内存地址
下面地址会?Compile Error / Runtime Crash / NSLog…?
@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
- (void)speak;
@end
@implementation Sark
- (void)speak {
NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Sark class];
void *obj = &cls;
[(__bridge id)obj speak];
}
@end
obj被转换成一个指向SarkClass的指针,然后使用id转换成了objc_object类型。obj现在已经是一个Sark类型的实例对象了。所以可以调用speak方法。
正确答案会输出 my name is <ViewController: 0x7ff6d9f31c50>
struct __rw_objc_super arg = {
(id)self,
(id)class_getSuperclass(objc_getClass("ViewController"))
};
所以按照viewDidLoad执行时各个变量入栈书讯从高到低为 self,_cmd,self.class,self,obj.
1. 第一个`self`和第二个`_cmd`是隐藏参数。
2. 第三个self.class和第四个self是[super viewDidLoad]方法执行时候的参数。
(id)class_getSuperclass(objc_getClass("ViewController"))?
Objc中的对象是一个指向ClassObject地址的变量,即id obj = &ClassObject,而对象的实例变量 `void *ivar = &obj + offset(N)`
原因还是和上面的类似。按`viewDidLoad`执行时各个变量入栈顺序从高到底为`self`,`_cmd`,`self.class`,`self`,`myName`,`obj`。`obj`往上偏移32位,就是self。