一、 instancetype和id的区别
1、instancetype在类型表示上,跟id一样,可以表示任何对象类型
2、instancetype只能用在返回值类型上,不能像id一样用在参数类型上
3、instancetype比id多一个好处:编译器会检测instancetype的真实类型
注:作为返回值时,凡是用id的地方,都建议换成instancetype
二、属性的本质
@property = ivar + getter + setter;
实例变量+ setter方法+ setter方法,也就是说使用@property 系统会自动生成成员变量、setter和getter方法;
三、@synthesize 和 @dynamic 分别有什么作用
- @property 有两个对应的词,分别是 @synthesize和 @dynamic。如果@synthesize 和 @dynamic 都没写,默认的就是 @syntheszie var = _var;
- @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
- @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter 方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
四、atomic
atomic的本意是指属性的存取方法是线程安全的,并不保证整个对象是线程安全的,在多线程中,atomic只保证getter、setter方法安全,并不保证其它操作,例如字符串拼接,数组移除元素等,并没有执行getter和setter方法,顾不是绝对安全的。
五、Block本质
- block本质上也是一个OC对象,它内部也有个isa指针
- block是封装了函数调用以及函数调用环境的OC对象
- block是封装函数及其上下文的OC对象
- auto变量block访问方式是值传递,auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;
- static变量block访问方式是指针传递,static变量一直保存在内存中,指针访问即可;
block不需要对全局变量捕获,都是直接采用取值的
- block里访问self会被捕获:self是当调用block函数的参数,参数是局部变量,self指向调用者
- block里访问成员变量会被捕获:成员变量的访问其实是self->xx,先捕获self,再通过self访问里面的成员变量
block三种类型:
1、__NSGlobalBlock __ 在数据区 :没有访问auto变量的block是__NSGlobalBlock __ ,放在数据段;调用copy操作后,什么也不做
2、__NSMallocBlock __ 在堆区 : [__NSStackBlock __ copy]操作就变成了__NSMallocBlock __ ;复制效果是:引用计数增加,副本存储位置是堆
3、__NSStackBlock __ 在栈区 : 访问了auto变量的block是__NSStackBlock __ ;调用copy操作后,复制效果是:从栈复制到堆;副本存储位置是堆
在ARC环境下,编译器会根据一下几种情况自动将栈上的block复制到堆上:
1、block作为函数返回值时
2、将block赋值给__strong指针时
3、block作为Cocoa API中方法名含有usingBlock的方法参数时
4、block作为GCD API的方法参数时
当block内部访问了对象类型的auto变量时:
如果block在栈空间,不管外部变量是强引用还是弱引用,block都会弱引用访问对象
如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用。
栈block:
a) 如果block是在栈上,将不会对auto变量产生强引用
b) 栈上的block随时会被销毁,也没必要去强引用其他对象
堆block:
1.如果block被拷贝到堆上:
a) 会调用block内部的copy函数
b) copy函数内部会调用_Block_object_assign函数
c) _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
2.如果block从堆上移除
a) 会调用block内部的dispose函数
b) dispose函数内部会调用_Block_object_dispose函数
c) _Block_object_dispose函数会自动释放引用的auto变量(release)
__block 修饰符作用:
- __block可以用于解决block内部无法修改auto变量值的问题
- __block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
- __block修改变量:age->__forwarding->age
- __Block_byref_age_0结构体内部地址和外部变量age是同一地址
六、NSCache和NSMutableDictionary的相同点与区别:
相同点:
NSCache和NSMutableDictionary功能用法基本是相同的。
区别:
NSCache是线程安全的,NSMutableDictionary线程不安全
NSCache线程是安全的,Mutable开发的类一般都是线程不安全的
当内存不足时NSCache会自动释放内存(所以从缓存中取数据的时候总要判断是否为空)
NSCache可以指定缓存的限额,当缓存超出限额自动释放内存
缓存限额:
缓存数量
@property NSUInteger countLimit;
缓存成本
@property NSUInteger totalCostLimit;
苹果给NSCache封装了更多的方法和属性,比NSMutableDictionary的功能要强大很多
七、runtime通过selector找到对应的IMP地址的两种方式
方式一:
类方法(假设有一个类 A)
class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));
实例方法
class_getMethodImplementation([A class],@selector(methodName));
方式二:
类方法
Method class_getClassMethod(Class cls, SEL name)
实例方法
Method class_getInstanceMethod(Class cls, SEL name)
最后调用IMP method_getImplementation(Method m) 获取IMP地址
八、避免设置圆角引起离屏渲染的优化
方式一:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"myImg"];
//开始对imageView进行画图
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
//使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
//结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
方式二:使用CAShapeLayer和UIBezierPath设置圆角
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(100,100,100,100)];
imageView.image = [UIImage imageNamed:@"myImg"];
UIBezierPath *maskPath=[UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer= [[CAShapeLayer alloc]init];
//设置大小
maskLayer.frame=imageView.bounds;
//设置图形样子
maskLayer.path=maskPath.CGPath;
imageView.layer.mask=maskLayer;
[self.view addSubview:imageView];
九、使用ShadowPath指定layer阴影效果路径避免离屏渲染
imageView.layer.shadowColor=[UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;
转载于:https://www.jianshu.com/p/caa4e6b5abad