0x01 - 概览
蓝牙技术最初由爱立信创制。技术始于爱立信公司的1994方案,它是研究在移动电话和其他配件间进行低功耗、低成本无线通信连接的方法。发明者希望为设备间的通讯创造一组统一规则(标准化协议),以解决用户间互不兼容的移动电子设备。从早期的1.0到目前最新的5.0版本,中间经过了十几年的发展。我们这里着重关注的是蓝牙4.0以及4.0之后的规范,蓝牙4.0于2010年7月7号推出,4.0之前的蓝牙统称为经典蓝牙
,4.0之后又加入了低功耗功能并且有效的传输距离拓展到了最大的60米。
蓝牙技术最初由爱立信创制。技术始于爱立信公司的1994方案,它是研究在移动电话和其他配件间进行低功耗、低成本无线通信连接的方法。发明者希望为设备间的通讯创造一组统一规则(标准化协议),以解决用户间互不兼容的移动电子设备。从早期的1.0到目前最新的5.0版本,中间经过了十几年的发展。我们这里着重关注的是蓝牙4.0以及4.0之后的规范,蓝牙4.0于2010年7月7号推出,4.0之前的蓝牙统称为经典蓝牙
,4.0之后又加入了低功耗功能并且有效的传输距离拓展到了最大的60米。
谈到蓝牙,很容易让人联想到蓝牙穿戴设备,好像听起来更靠近硬件层一些。苹果其实对iOS和OSX上的蓝牙已做了一层很好的封装,看过CoreBluetooth Framework的大致API之后,基本上就将其流程明白个大概。难点在于理解其工作模式和理清一些关键概念,比如Peripehral
, Central
, Service
, characteristics
等等,不要被这些陌生的单词吓到,网络协议的应用大多脱不了CS的架构模型,这里和大家一起对照传统的Client/Server架构来梳理下iOS和OSX上CoreBluetooth的重要知识点。我画了一张图,方便大家一目了然的明白CoreBluetooth的工作原理。
这一节讲讲外接设备(Peripheral)
,主要是关于CBPeripheralManager
这个类的使用。
常见名称和缩写
MFI
: make for ipad ,iphone, itouch 专们为苹果设备制作的设备
BLE
: buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE
1.设置假的间距,我们在tableviewcell
的contentView
上添加一个view,比如让其距离上下左右的距离都是10
;这个方法是最容易想到的;
看以下习题前可以先看看下面的别人整理好的面试题:
iOSInterviewQuestions【强烈推荐看】
习题来自:iOS-InterviewQuestion-collection
@autoreleasrPool
的释放时机?
在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop
迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
参考:
自动引用计(ARC)
数应该遵循的原则?
不能使用 retain/release/retainCount/autorelease
不能使用 NSAllocateObject/NSDeallocateObject
须遵守内存管理的方法命名规则
不要显示调用dealloc
使用 @autorelease 块代替NSAutoreleasePool
不能使用区域(NSZone)
对象型变量不能作为C语言结固体的成员
显示转换“id”和”void *”
参考:
访问__weak
修饰的变量,是否已经被注册在了 @autoreleasePool
中?为什么?
在访问 __weak
修饰的变量时,实际上必定会访问注册到 Autorelease Pool
的对象。如下来年两段代码是相同的效果:
1 | id __weak obj1 = obj0; |
为什么会这样呢?因为 __weak
修饰符只持有对象的弱引用,而在访问对象的过程中,该对象有可能被废弃,如果把被访问的对象注册到 Autorelease Pool 中,就能保证 Autorelease Pool 被销毁前对象是存在的。
参考:
ARC 的 retainCount 怎么存储的?
the reference counts for most objects are stored in hash tables. Have a look at the _objc_rootRetain
function in runtime/objc-arr.mm
参考:
Where is the retain count stored for NSObjects in Objective C
简要说一下 @autoreleasePool 的数据结构
1 | AutoreleasePoolPage { |
magic用来校验AutoreleasePoolPage结构是否完整;
next指向第一个可用的地址;
thread指向当前的线程;
parent指向父类
child指向子类
参考:
__weak
和 _Unsafe_Unretain
的区别
weak与unsafe_unretained的区别在于,weak
会将被释放指针赋值为nil,而unsafe_unretained
则会成为野指针。
为什么已经有了 ARC ,但还是需要 @AutoreleasePool 的存在?
ARC 并不是舍弃了 @autoreleasepool
,而是在编译阶段帮你插入必要的 retain
/release
/autorelease
的代码调用。
所以,跟你想象的不一样,ARC 之下依然是延时释放的,依然是依赖于 NSAutoreleasePool
,跟非 ARC 模式下手动调用那些函数本质上毫无差别,只是编译器来做会保证引用计数的正确性。
__weak
属性修饰的变量,如何实现在变量没有强引用后自动置为 nil
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
说一下对 retain,copy,assign,weak,_Unsafe_Unretain 关键字的理解
assign
表明 setter 仅仅是一个简单的赋值操作,通常用于基本的数值类型,例如CGFloat和NSInteger。
weak
表明属性定义了一个非拥有者关系。当给属性设定一个新值的时候,这个值不会进行 retain,旧值也不会进行 release, 而是进行类似 assign 的操作。不过当属性指向的对象被销毁时,该属性会被置为nil。
strong
表明属性定义一个拥有者关系。当给属性设定一个新值的时候,首先这个值进行 retain ,旧值进行 release ,然后进行赋值操作。
copy
类似于 strong,不过在赋值时进行 copy 操作而不是 retain 操作。通常在需要保留某个不可变对象(NSString最常见),并且防止它被意外改变时使用。
unsafe_unretained
的语义和 assign 类似,不过是用于对象类型的,表示一个非拥有(unretained)的,同时也不会在对象被销毁时置为nil的(unsafe)关系。
参考:
NSString属性什么时候用copy,什么时候用strong?
ARC 在编译时做了哪些工作
根据代码执行的上下文语境,在适当的位置插入 retain
,release
ARC 在运行时做了哪些工作
待查~
函数返回一个对象时,会对对象 autorelease 么?为什么?
参考 arc 内存管理要点 这篇。
说一下什么是 悬垂指针?什么是 野指针?
悬垂指针(dangling pointer)一般是说指向已经被释放的自由区内存(free store)的指针,野指针(wild pointer)则一般是未经初始化的指针。前者曾经有效过,后者从未有效过。
参考:
悬垂指针(Dangling pointer)和野指针(Wild pointer)
内存管理默认的关键字是什么?
strong
内存中的5大区分别是什么?
内存5大区:堆,栈,方法区,全局区,常量区
栈:不需要手动管理内存,会自动清理栈中的内存
堆: 需要手动管理内存
静态区:又称全局区
常量区: 储存常量的地方
方法区: 存放函数体的二进制代码
是否了解 深拷贝 和 浅拷贝 的概念,集合类深拷贝如何实现?
深拷贝
是内存拷贝,指向的是不同的内存浅拷贝
是指针拷贝,指向的是同一块内存
集合类深拷贝是通过归档、解档实现。
BAD_ACCESS 在什么情况下出现?
类对象的数据结构?
Objective-C类是由Class
类型来表示的,它实际上是一个指向objc_class
结构体的指针。它的定义如下:
1 | typedef struct objc_class *Class; |
查看objc/runtime.h
中objc_class
结构体的定义如下:
1 | struct objc_class { |
在这个定义中,下面几个字段是我们感兴趣的
isa
:需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class
里面也有一个isa
指针,它指向metaClass
(元类),我们会在后面介绍它。super_class
:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class
为NULL。cache
:用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache
就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache
列表中,下次调用的时候runtime就会优先去cache中查找,如果cache
没有,才去methodLists
中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。version
:我们可以使用这个字段来提供类的版本信息。这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。参考:
Objective-C Runtime 运行时之一:类与对象
实例对象的数据结构?
实例对象都是一个id
类型的对象,查看objc.h
中对id
的描述:
1 | #if !OBJC_TYPES_DEFINED |
查看源文件,可以看出id
其实就是一个指向objc_object
结构体指针,它包含一个Class
isa 成员。所以实例对象的数据结构实际上就是一个Class
的数据结构。
元类对象的数据结构?
meta-class 是 Class 对象的类。每个 Class 都有个不同的自己的 meta-class(因此每个 Class 都可以有一个自己不同的方法列表)。也就是说每个类的 Class 不完全相同。
参考:
Objective-C 中的 Meta-class 是什么?
Category
的实现原理?以及如何实现添加一个属性?
参考:
如何运用 Runtime
字典转模型?
参考:
简单的版本如上,其他还有一些关键字的不能作为key值得还要映射一下的。还有嵌套的等等其他的要考虑。
Category
有哪些用途?
1)无需创建继承类,实现对已有类扩展。并且可以被扩展的类的所有子类自动继承。
2)可以用来修复没有源码类的bug。
3)对于一个类多个开发人员维护的情况,可以根据不同用途创建不同分类。
注意点:
1)不能在分类中重写系统方法,因为会把系统的功能给覆盖掉,而且分类中不能调用super。但是,这种情况可以用来修复,没有源码的类中方法有Bug的情况。
Category
和 Extension
有什么区别?
1)Category
的加载在运行时,Extension
的加载在编译时。
2) Extension
不能给没有源码的类添加方法。
3) Extension
是一个匿名的 Category
。
说一下 Method Swizzling
? 说一下在实际开发中你在什么场景下使用过?
参考:
关于Method Swizzling
踩过的坑,
参考这里
如何实现动态添加方法和属性?
1 | //动态创建类 |
上述代码,我们通过objc_allocateClassPair()
创建了一个继承NSObject
的person
的子类,然后通过objc_registerClassPair()
这个函数注册了person
类,下面我们就可以使用这个类了,使用之前我们在给person
类中添加一个name
属性和sayHi:
方法,分别通过class_addIvar()
和class_addMethod()
来添加,接下来我们要实现我们添加的sayHi:
方法,如下:
1 | //这个函数必须写,要不然xcode会报错,实际运行的时候,这个函数是不会调用的 |
static void sayHi(id self, SEL _cmd, NSString *name)
这个就是我们运行时候调用的方法,其中self
使我们使用person
创建的对象Tom
,_cmd
是调用的方法名,name
就是传过来的参数,如果有多个参数,可以写成static void sayHi(id self, SEL _cmd, NSString *name,...)
省略号可以填写你愿意添加的参数。
这样我们就利用runtime动态的创建了一个person类,包括了name属性和sayHi:方法,运行如结果如下:
1 | RunTimeDemo[46329:7242266] hello Jeck,my name is Tom |
参考:
说一下对 isa 指针的理解, 对象的isa 指针指向哪里?(注意区分不同对象)
网上有2种说法:
1)isa
是指向元类的指针,不了解元类的可以看 Objective-C 中的 Meta-class 是什么?
2) 任何直接或间接继承了NSObject
的类,它的实例对象(instacne objec)中都有一个isa
指针,指向它的类对象(class object)。这个类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等。
参考:
Objective-C 中的 Meta-class 是什么?
Obj-C
中的类信息存放在哪里?
类方法存储在元类。
当前类的信息都在 data
里。看看源码就知道了。
一个 NSObject 对象占用多少内存空间?
通过 size_t class_getInstanceSize ( Class cls )
接口可以获取NSObject
对象占用内存空间大小。
1 | size_t z = class_getInstanceSize([NSObject class]); |
说一下对 class_rw_t
的理解?
rw
代表可读可写。
ObjC
类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t
中:
1 | struct class_rw_t { |
其中还有一个指向常量的指针 ro
,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议
参考:
说一下对 class_ro_t
的理解?
储了当前类在编译期就已经确定的属性、方法以及遵循的协议。
1 | struct class_ro_t { |
参考:
说一下 Runtime
消息解析和转发
先看一下流程图:
1、根据 text 和字体大小返回 size
1 | CGSize topTilteSize = [KGCommonTools sizeForString:topTilte.text font:topTilte.font]; |
2、ceil(xx)
函数
函数名: ceil
用 法: double ceil(double x);
功 能: 返回大于或者等于指定表达式的最小整数
头文件:math.h
3、UIButton 的一个分类,扩大 button 本身的点击响应范围
1 | #import <UIKit/UIKit.h> |
1 | #import "UIButton+EnlargeEdge.h" |
使用:
1 | [cell.downloadBtn setEnlargeEdgeWithTop:15 bottom:15 left:7.5 right:15]; // 扩大点击范围 |
参考链接:
4、解决阴影和圆角不能同时共存的问题
参考:
5.
关于 Method Swizzling
其实已经算是挺熟悉了的,但今天在用的时候遇到了一个坑,觉得还是有必要记录一下的。