锁 OSSpinLock 自旋锁 实现机制:忙等 操作重点:原子操作
1.自旋锁
2.互斥锁
3.读写锁
4.信号量
5.条件锁
6.递归锁
需导入头文件:
import <libkern/OSAtomic.h>
pragma mark --加锁
-(void)addLock{
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"准备上锁");
OSSpinLockLock(&oslock);
NSLog(@"已经上锁");
OSSpinLockUnlock(&oslock);
NSLog(@"解锁成功");
});
}
锁 OSSpinLock 自旋锁 实现机制:忙等 操作重点:原子操作 https://www.jianshu.com/p/c8ed92cc43b6
iOS哪些操作会造成内存泄漏
1.Block循环引用(比如mj的下啦刷新需要用weakself)cmp.refreshingBlock = refreshingBlock;这一句,这里的refreshingBlock是属于MJRefreshHeader的强引用属性
2.delegate循环引用问题
3.NSTimer循环引用
4.非OC对象内存处理
比如常用的滤镜操作调节图片亮度 CGImageRelease(ref);//非OC对象需要手动内存释放
5.地图类处理
使用完毕时将地图、代理等滞空为nil,注意地图中标注(大头针)的复用,并且在使用完毕时清空标注数组等。
6.大次数循环内存暴涨问题
该循环内产生大量的临时对象,直至当前runloop休眠前才释放掉,可能导致内存泄漏,解决方法为在循环中创建自己的autoReleasePool,及时释放占用内存大的临时变量,减少内存占用峰值。
通过调用autorelease方法把释放对象的任务交给Autoreleasepool对象,当Autoreleasepool对象执行到[pool drain]方法的时候会对自动释放池中所有的对象执行一次release操作,然后+ (instancetype)student方法中创建的对象引用计数就会被-1了,如果引用计数变为0了,对象自然就释放了
调用方获取对象以后自己retain持有一下对象,防止使用期间对象被释放了,用完release释放一下
for (int i = 0; i < 100000; i++) {
@autoreleasepool {
NSString *string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@", string);
}
}
wk的js交互
self.methodArray = [[NSArray alloc]initWithObjects:XCJSGoToHousingDetails,XCJSVrviewer,nil];
WKWebView *wkWebView = [self.curVC.view viewWithTag:WEBVIEW_TAG];
for (int i = 0; i < self.methodArray.count; i++) {
[wkWebView.configuration.userContentController addScriptMessageHandler:[[weakScriptMessageDelegate alloc] initWithDelegate:self.curVC] name:self.methodArray[i]];
}
//WKScriptMessageHandler协议方法
-
(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
//codeNSLog(@"%@",NSStringFromSelector(_cmd));
[self.wkJSSDK setWKScriptMessage:message];
}
cookie都存在哪里 多个wkwebview的cookie怎么同步的
怎么样保护一个线程安全
内存管理机制(例如一个列表 一直滑动什么时候会崩溃 给app的最大内存分配是虚拟内存还是实际内存)
设计模式 单例模式 代理模式 Cocoa模式等等
其中包括常见的软件设计原则,责任链、适配器、桥接、命令、单例、策略模式等等,不要告诉我你只是看了几本书,面试官会让你结合实际业务场景,现场考察你对设计模式的运用和理解的。
组件化
1.runtime https://www.jianshu.com/p/c3a5ffcd856b
runtime
1.消息传递
首先通过对象的isa指针找到对应的class(对象都是结构体里面包含isa指针,成员变量等)
然后在class的methodlist中找对应的方法
如果class中找不到,就继续往它的superclass中找
一旦找到这个函数,就去执行它的实现IMP
如果找不到先进入+(BOOL)resolveInstanceMethod:(SEL)sel
方法 可以
+(BOOL)resolveInstanceMethod:(SEL)sel
{
// 以"@@:"作为方法签名类型返回。这里第一字符@代表函数返回类型NSString,第二个字符@代表self的类型id,第三个字符:代表_cmd的类型SEL。
if (sel == @selector(foo:)) {
class_addMethod([self class], sel, (IMP)fooMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void fooMethod(id obj,SEL _cmd){
NSLog(@"fooMethod");
}
如果这个方法没有实现替代返回yes继续
备用接收者
-(id)forwardingTargetForSelector:(SEL)aSelector
-(id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector==@selector(foo)) {
return [Person new];
}
return [super forwardingTargetForSelector:aSelector];
}
这个方法返回nil的话
进入-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = anInvocation.selector;
// Person *p = [Person new];
Person *p = [[Person alloc] init];
if ([p respondsToSelector:sel]) {
[anInvocation invokeWithTarget:p];
}
else{
[self doesNotRecognizeSelector:sel];
}
}
如果再没有实现的话就报错了
关联对象(Objective-C Associated Objects)给分类增加属性
分类不能添加属性主要是因为不能实现set和get方法
import <objc/runtime.h>
static char kDefaultColorKey;
//set方法
-(void)setDefaultColor:(UIColor *)defaultColor
{
objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
//get方法
-(UIColor *)defaultColor
{
return objc_getAssociatedObject(self, &kDefaultColorKey);
}
方法魔法(Method Swizzling)方法添加和替换和KVO实现
方法添加
class_addMethod([self class], sel, (IMP)fooMethod, "v@:")
方法替换
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL oldSel = @selector(viewDidLoad);
SEL newSel = @selector(viewDidLoadNew);
Method oldM = class_getInstanceMethod(class, oldSel);
Method newM = class_getInstanceMethod(class, newSel);
// 判断newM方法是否存在
BOOL didAddmethod = class_addMethod(class, oldSel, method_getImplementation(newM), method_getTypeEncoding(newM));
if (didAddmethod) {
class_replaceMethod(class, newSel, method_getImplementation(oldM), method_getTypeEncoding(oldM));
}else{
method_exchangeImplementations(oldM, newM);
}
});
}
注意点方法需要在load里面执行 单例只执行一次
- load 作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法。其中关键字有这么几个:
文件刚加载
main 函数之前
钩子方法
实现NSCoding的自动归档和自动解档
原理描述:用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。
核心方法:在Model的基类中重写方法:
import <objc/runtime.h>
(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{
unsigned int outCount;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++)
{
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
}
return self;
}(void)encodeWithCoder:(NSCoder *)aCoder
{
unsigned int outCount;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++)
{
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
// 自动归档/解档
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"PersonInfo"];
if (data) {
// 解档使用
Person *person = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"name: %@", person.name);
} else {
Person *person = [Person new];
person.name = @"devZhang";
person.age = @(35);
person.company = @"ShengXue";
person.job = @"iOSDev";
person.address = @"龙岗坂田国际中心";
// 归档存储
data = [NSKeyedArchiver archivedDataWithRootObject:person];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"PersonInfo"];
}
实现字典和模型的自动转换(MJExtension)
原理描述:用runtime提供的函数遍历Model自身所有属性,如果属性在json中有对应的值,则将其赋值。
核心方法:在NSObject的分类中添加方法-
(instancetype)initWithDict:(NSDictionary *)dict {
if (self = [self init]) {
//(1)获取类的属性及属性对应的类型
NSMutableArray * keys = [NSMutableArray array];
NSMutableArray * attributes = [NSMutableArray array];
/*
* 例子
* name = value3 attribute = T@"NSString",C,N,V_value3
* name = value4 attribute = T^i,N,V_value4
*/
unsigned int outCount;
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];
//通过property_getName函数获得属性的名字
NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
[keys addObject:propertyName];
//通过property_getAttributes函数可以获得属性的名字和@encode编码
NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
[attributes addObject:propertyAttribute];
}
//立即释放properties指向的内存
free(properties);//(2)根据类型给属性赋值 for (NSString * key in keys) { if ([dict valueForKey:key] == nil) continue; [self setValue:[dict valueForKey:key] forKey:key]; }
}
return self;
}
2.kvc/kvo
kvo机制
kvo的作用主要是观察某个对象的属性变化
kvo的实现原理 通过runtime 在观察某个对象的时候,kvo动态创建了一个当前类的子类,并且为这个新的子类重写了观察属性的setter方法
重写的setter方法会负责在调用原setter方法前后,通知所有观察对象属性值的更改情况
kvo
1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么)
利用runtimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
当修改instance对象的属性时,会调用Foundation的NSSetXXXValveAndNotify函数
willChangeValueForkey
父类原来的setter
didChangeValueForKey
内部会触发监听器(obserer)的监听方法(observeValueForKeyPath:ofObject:change:context)
2.如何手动触发KVO?
手动调用willChangeValueForkey和didChangeValueForKey
3.直接修改成员变量会触发KVO吗?
不会触发KVO,因为只有触发set方法才会触发(变量=不会触发 .变量=才会触发)
kvc
1.基本概念
可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。
2.赋值方法
(nullable id)valueForKey:(NSString *)key;
(void)setValue:(nullable id)value forKey:(NSString *)key;
(nullable id)valueForKeyPath:(NSString *)keyPath;
(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
3.赋值原理
找到对应的key,然后将一个已经准备好的值赋过去
方法- (nullable id)valueForKey:(NSString *)key;
内部流程
1.程序优先调用set<Key>:属性值方法,代码通过setter方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则
2.如果没有找到setName:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为<key>的成员变量,无论该变量是在类接口处定义,还是在类实现处定义,也无论用了什么样的访问修饰符,只在存在以<key>命名的变量,KVC都可以对该成员变量赋值。
3.如果该类即没有set<key>:方法,也没有_<key>成员变量,KVC机制会搜索is<Key>的成员变量。
4.和上面一样,如果该类即没有set<Key>:方法,也没有<key>和_is<Key>成员变量,KVC机制再会继续搜索<key>和is<Key>的成员变量。再给它们赋值。
5.如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
6.特别需要注意的是:如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。
https://www.jianshu.com/p/5273c8e29799
https://www.jianshu.com/p/c3a5ffcd856b/https://www.jianshu.com/p/c3a5ffcd856b
3.线程/线程锁 原子安全
4.gcd
用过哪些api
5.内存管理
哪些方法和操作会导致内存泄露
6.崩溃异常怎么修复
7.@synthesize 和 @dynamic分别有什么作用
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法
@dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成(当然对于readonly的属性只需提供getter即可)
为什么block要使用copy而不是strong或者其他属性修饰?
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈上的,而不是在堆上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。因为栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,
因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。
category的加载处理过程
1.通过Runtime加载某个类的所有Category数据
2.把所有Category的方法,属性,协议数据,合并到一个大数组中,后面参与编译的Category数据,会在数组的前面
3.将合并后的分类数据(方法,属性,协议)插入到类原来数据的前面
+load方法会在runtime加载类,分类时调用 在mian函数之前调用
每个类,分类的+load,在程序运行过程中只调用一次
调用顺序
1.先调用类的+load
按照编译先后顺序调用(先编译,先调用)
调用子类的+load之前会先调用父类的+load
2.再调用分类的+load
按照编译先后顺序调用(先编译,先调用)