iOS 知识点小集

一、 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对象
iOS 知识点小集_第1张图片
image.png
  • auto变量block访问方式是值传递,auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;
  • static变量block访问方式是指针传递,static变量一直保存在内存中,指针访问即可;
  • block不需要对全局变量捕获,都是直接采用取值的


    iOS 知识点小集_第2张图片
    image.png
  • 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];

方式二:使用绘图技术

- (UIImage *)circleImage {
    // NO代表透明
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    // 获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一个圆
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 将图片画上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    return image;
}

九、使用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;

十、关于自定义View的初始化方法

通常我们会创建私有方法createUI方法来创建当前自定义View所需要的子View。那上述所说的createUI应该放在自定义View的哪个方法中呢?
1、init?
2、initWithFrame?
3、还是为了考虑外部创建自定义View的方式不同,在init与initWithFrame方法中均调用createUI方法?

- (instancetype)init {
    if (self = [super init]) {
        [self createUI];
    }
    return self;
}

- (void)createUI {
    [self addSubview:self.testView];
}

- (UIView *)testView {
    if (!_testView) {
        _testView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        _testView.backgroundColor = [UIColor redColor];
    }
    return _testView;
}

createUI方法最好在initWithFrame中调用,外部使用init或initWithFrame均可以正常执行createUI方法。不要在自定义View中同时重写init与initWithFrame并执行相同视图布局代码。会导致布局代码(createUI)执行多次。
原因:
1、动态查找到CustomView的init方法
2、调用[super init]方法
3、super init方法内部执行的的是[super initWithFrame:CGRectZero]
4、若super发现CustomView实现了initWithFrame方法
5、转而执行self(CustomView)的initWithFrame方法
6、最后在执行init的其余部分

关于addSubview

我们接着问题一自定义View的初始化方法来说,如果同时在init与initWithFrame中同时调用了createUI方法,createUI方法执行了多次,也就是说重复多次添加了self.testView。那是否会重复添加多个View层呢?并不会,重复多次添加同一个View并不会产生多层级的情况。
原因:
View有且仅有一个父视图,如果新的父视图与原父视图不一样,会将View在原视图中移除,添加到新视图上。因此同一父视图重复添加同一个View并不会产生多层级。
结论:
若父视图重复添加同一子视图,并不会产生多层级情况。因为此例中testView是以懒加载的形式创建,所以self每次添加的均为同一个View,但如果在createUI中以UIView *testView = [UIView alloc] initWithFrame的形式创建,那就会创建出多层级的View。自定义View的子视图最好以懒加载形式创建,可避免因其他书写不当导致的异常

关于layoutSubviews

1、自定义视图的init方法并不会调用layoutSubviews
2、苹果声明不要直接调用layoutSubviews方法,如果需要更新,应该调用setNeedsLayout方法,视图会在下一次绘制后更新。如果需要立即更新视图,需要执行layoutIfNeeded方法
3、因为layoutSubviews调用比较频繁,因此若无特殊需求(文档所述为执行精确的子视图布局时可使用),不用重写layoutSubviews方法。

十一、 ATS

1、iOS9中新增App Transport Security(简称ATS)特性, 让原来请求时候用到的HTTP,全部都转向TLS1.2协议进行传输。
2、这意味着所有的HTTP协议都强制使用了HTTPS协议进行传输。
3、如果我们在iOS9下直接进行HTTP请求是会报错。系统会告诉我们不能直接使用HTTP进行请求,需要在Info.plist中控制ATS的配置。
"NSAppTransportSecurity"是ATS配置的根节点,配置了节点表示告诉系统要走自定义的ATS设置。
"NSAllowsAritraryLoads"节点控制是否禁用ATS特性,设置YES就是禁用ATS功能。
4、有两种解决方法,一种是修改配置信息继续使用以前的设置。
另一种解决方法是所有的请求都基于基于"TLS 1.2"版本协议。(该方法需要严格遵守官方的规定,如选用的加密算法、证书等)

ATS默认的条件

1、服务器TLS版本至少是1.2版本
2、连接加密只允许几种先进的加密
3、证书必须使用SHA256或者更好的哈希算法进行签名,要么是2048位或者更长的RSA密钥,要么就是256位或更长的ECC密钥。

AFSecurityPolicy,内部有三个重要的属性,如下:

AFSSLPinningMode SSLPinningMode; //该属性标明了AFSecurityPolicy是以何种方式来验证
BOOL allowInvalidCertificates; //是否允许不信任的证书通过验证,默认为NO
BOOL validatesDomainName; //是否验证主机名,默认为YES
"AFSSLPinningMode"枚举类型有三个值,分别是AFSSLPinningModeNone、AFSSLPinningModePublicKey、AFSSLPinningModeCertificate。

"AFSSLPinningModeNone"代表了AFSecurityPolicy不做更严格的验证,"只要是系统信任的证书"就可以通过验证,不过,它受到allowInvalidCertificates和validatesDomainName的影响;

"AFSSLPinningModePublicKey"是通过"比较证书当中公钥(PublicKey)部分"来进行验证,通过SecTrustCopyPublicKey方法获取本地证书和服务器证书,然后进行比较,如果有一个相同,则通过验证,此方式主要适用于自建证书搭建的HTTPS服务器和需要较高安全要求的验证;

"AFSSLPinningModeCertificate"则是直接将本地的证书设置为信任的根证书,然后来进行判断,并且比较本地证书的内容和服务器证书内容是否相同,来进行二次判断,此方式适用于较高安全要求的验证。

如果HTTPS服务器满足ATS默认的条件,而且SSL证书是通过权威的CA机构认证过的,那么什么都不用做。如果上面的条件中有任何一个不成立,那么都只能修改ATS配置。

十二、isinf()、isnan()

isfinite()测试某个浮点数是不是有限的数
isfinite(float x);
isfinite(double x);
isfinite(long double x);
 
isinf()测试某个浮点数是否是无限大
isinf(float x);
isinf(double x);
isinf(long double x);
 
isnan()测试某个浮点数是否是 非数字
isnan(float x);
isnan(double x);
isnan(long double x);
 
isnormal()测试某个浮点数是否被规格化
isnormal(float x);
isnormal(double x);
isnormal(long double x);
 
signbit()测试某个浮点数是否为负数
signbit(float x);
signbit(double x);
signbit(long double x);

十三、FOUNDATION_STATIC_INLINE

FOUNDATION_STATIC_INLINE为内联函数的宏定义。

#define FOUNDATION_STATIC_INLINE static __inline__

内联函数完全可以取代表达式形式的宏定义。

为什么要用内联函数:
效率来看

函数之间调用,是内存地址之间的调用、当函数调用完毕之后还会返回原来函数执行的地址。函数调用将会有时间开销。
内联函数在汇编中没有call语句。取消了函数的参数压栈

相比表达式形式的宏定义

因为inline内联函数也是函数、不需要预编译。
调用时候会首先检查它的参数的类型、保证调用正确。
可以使用所在类的保护成员及私有成员。

需要注意

内联函数中尽量不要使用诸如循环语句等大量代码、可能会导致编译器放弃内联动作。
内联函数的定义须在调用之前。

十四、ps和pt转换

  • px:相对长度单位。像素(Pixel)。(PS字体)
  • pt:绝对长度单位。点(Point)。(iOS字体)
pt=(px/96)*72。

十五、控制台日志输出不全

定义日志输出宏:

#define DLog(FORMAT, ...) printf("***********自定义日志全部输出***********:\n调用方法: %s \n所在行数:[Line %d] \n%s \n ",__PRETTY_FUNCTION__, __LINE__,[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);

十六、监听来电接听状态

导入头文件:

#import 
#import 

强引用:

@property (nonatomic,strong) CTCallCenter *callCenter;

创建实例监听状态:

    self.callCenter = [[CTCallCenter alloc] init];
    self.callCenter.callEventHandler=^(CTCall* call){
        if (call.callState == CTCallStateDisconnected){
            NSLog(@"挂断电话Call has been disconnected");
        }else if (call.callState == CTCallStateConnected){
            NSLog(@"电话通了Call has just been connected");
        }else if(call.callState == CTCallStateIncoming){
            NSLog(@"来电话了Call is incoming");
        }else if (call.callState ==CTCallStateDialing){
            NSLog(@"正在播电话call is dialing");
        }
        else{
            NSLog(@"Nothing is done");
        }
    };

十七、设置返回按钮文字消失

方式一:
此方法是设置文字为clearColor,会影响其他想要正常显示的文字一并看不到,所以不实用

if (@available(iOS 11.0, *)) {
    // 设置返回按键文字消失
    [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
}

方式二:
设置文字偏移量

[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-1000, 0) forBarMetrics:UIBarMetricsDefault];

十八、语音播报功能

系统原生方式法,例如支付到账语音提醒
导入头文件

#import 
#pragma mark 语音播报
-(void)voicePlay{
    AVSpeechSynthesizer *av = [[AVSpeechSynthesizer alloc]init];
    AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];
    AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:@"收款100元"];
    utterance.rate = AVSpeechUtteranceDefaultSpeechRate;
    utterance.voice = voice;
    [av speakUtterance:utterance];
}

你可能感兴趣的:(iOS 知识点小集)