1、NSString能不能被继承,为什么?
不可以,先看一下NSString的alloc方法
@class NSPlaceholderString;
@interface NSString:(NSObject)
+ (id) alloc;
@ end
@implementation NSString
+(id) alloc {
if ([self isEquals:[NSString class]]) {
return [NSPlaceholderString alloc];
}
else
return [super alloc];
}
@end
@interface NSPlaceholderString:(NSString)
@end
在alloc方法中我们可以看到,当只用NSString调用alloc的时候,由于self == [NSString class],所以这时返回的是NSPlaceholderString的类对象;而使用其他类(比如派生类)调用alloc时,返回的是super的 alloc,这里也就是[NSObject alloc],而NSObject的alloc方法返回的是调用类的类对象,所以在我们用我们自己的XXString就是XXString类的类对象了所以没有NSString 的一些方法了。
2、如何给类如UILabel添加方法,底层是什么
使用category对类添加方法,Objective-C 中的 Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。
好处:
a)可以减少单个文件的体积
b)可以把不同的功能组织到不同的category里
c)可以由多个开发者共同完成一个类
d)可以按需加载想要的category 等等。
category的结构体
所有的OC类和对象,在runtime层都是用struct表示的,category也不例外,在runtime层,category用结构体category_t.
structcategory_t {
constchar*name;//类的名字(name)
classref_t cls;//类(cls)
struct method_list_t *instanceMethods; //category中所有给类添加的实例方法的列表(instanceMethods)
struct method_list_t *classMethods;//category中所有添加的类方法的列表(classMethods)
struct protocol_list_t *protocols; //category实现的所有协议的列表(protocols)
struct property_list_t *instanceProperties;//category中添加的所有属性(instanceProperties)
};
添加方法是会将方法添加进方法列表中,所以实现了添加方法的作用
注意,在category中可以有属性(property),但是该属性只是生成了getter和setter方法的声明,并没有产生对应的实现,更不会添加对应的实例变量。如果想为实例对象添加实例变量,可以尝试使用关联引用技术
3、当category方法与原类方法相同时,调用方法,会执行原类方法还是category的方法?为什么
执行category的实现方法
添加方法列表的时候是后添加的在新形成的列表前部,所以后编译的在调用时会“覆盖”前面已编译的方法。其实方法本身并没有被覆盖,只是调用的时候是从上而下查找方法列表,当运行时找到对应的方法名后就去忙着调用了,并不会管后面的同名方法。
4、获取view所在的viewcontroller
-(UIViewController*)viewController{
for(UIView*next =self.superview;next;next = next.superview){
UIResponder*nextResponder = [next nextResponder];
if([nextResponderisKindOfClass:[UIViewControllerclass]]){
return(UIViewController*)nextResponder;
}
}
return nil;
}
5、工厂模式和抽象工厂模式
https://www.jianshu.com/p/8cf0b9804e1b
6、weak底层实现原理
weak表其实是一个哈希表,key是所指对象的指针,value是weak指针的地址数组。(value是数组的原因是:因为一个对象可能被多个弱引用指针指向)
Runtime维护了一张weak表,用来存储某个对象的所有的weak指针。
weak原理实现过程三步骤
1.初始化开始时,会调用objc_initWeak函数,初始化新的weak指针指向对象的地址
2.紧接着,objc_initWeak函数里面会调用objc_storeWeak() 函数,objc_storeWeak() 函数的作用是用来更新指针的指向,创建弱引用表。
3.在最后会调用clearDeallocating函数。而clearDeallocating函数首先根据对象的地址获取weak指针地址的数组,然后紧接着遍历这个数组,将其中的数组开始置为nil,把这个entry从weak表中删除,最后一步清理对象的记录。
7、OC对象怎么实现引用计数的?
sideTable 其实是一个 hash 表,下面挂了很多的 sideTable,sidetable 包括自旋锁(spinlock_t),引用计数表(refcountMap),弱引用表(weak_table_t)。
sidetables 为什么是多张表,而不是一张表?:
如果只有一张表,如果想操作某一个兑现的印象计数,由于不同的对象是在不同的线程操作,由于不同线程需要来操作这张表,所以就有资源访问的问题,那么就需要对这张大表进行加锁操作,如果成千上万对自己进行引用计数操作,那么需要加锁排队,就会有效率问题,所以系统引用了 “分离锁” 概念,比如 A,B同时进行操作的话,可以并发进行,因为A,B,在不同的表中。
如果实现快速分流?找到当前对象在哪张表中?:
sideTables 其实是一张 hash 表,key(对象指针)-》hash函数-》value(sideTable),通过这个hash计算之后,就可以计算出当前对象在哪个hash表中,也就找到了对应的sideTable
hash 查找:
给定一个内存地址,通过hash计算就可以得到数组的下表地址,f(ptr) = ptr%arr.count,比如内存地址为1,通过上面的就可以找到在数组中的位置
散列表的数据结构:
自旋锁:忙等,如果锁已被其他线程获取,那么当前线程会自己去不断的获取是否被释放,直到其他线程释放,适用于轻量访问,如+1,-1。
引用计数表(refcountmap):其实就是hash查找,提高查找效率,插入和查找通过同一个hash函数来获取,避免了循环遍历。ptr->hash->size_t,其中的size_t就是引用计数值,比如用64位存储,第一位表示(weakly_referenced),表示对象是否存在弱引用,下一位表示当前对象是都正在dealoc(deallocating),剩下的位表示引用计数值。
8、调用野指针会报错吗?
不一定
如果对象指针指向内存已经被其他对象覆写占用,那么会crash,如果没有没覆写,调用依然可以正确执行。