一、 defin定义的宏和const定义的常量的区别
- define定义的常量,程序在预处理阶段只是直接用define定义的内容进行了替换,没有数据类型,所以编译时不能进行数据类型检验,并且在字符替换可能会产生意料不到的错误(边际效应)。const定义的常量有数据类型,在编译时进行严格的类型检验,可以避免出错。
- 在程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。const定义的常量,在程序运行时在常量表中,系统为它分配内存。
- 在程序执行过程中const定义的只读变量在程序运行过程中只有一份拷贝,而 #define定义的常量在内存中可能有若干个拷贝,因此const可以节省空间,避免不必要的内存分配。
- 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
二、static关键字的作用
- 用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变。
- 用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
- 使用static用于函数定义时,使得函数只在本文件内部有效,对其他文件是不可见的,而且在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
- static变量程序运行过程中只被初始化一次。
三、内存分区情况
- 代码区
存放函数二进制代码 - 数据区
系统运行时申请内存并初始化,系统退出时由系统释放,存放全局变量,静态变量,常量 - 堆区
1、通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释放,容易产生内存泄漏。
2、频繁的new/delete会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 - 栈区
1、由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
2、先进先出,一一对应,不会有单独的一块内存快从栈中间弹出。
四、OC中的反射机制
- class反射
//通过类名的字符串形式实例化对象
Class class = NSClassFromString(@"NSObject");
NSObject *object = [[class alloc]init];
//将类名变为字符串
NSString *className = NSStringFromClass([NSObject class]);
- SEL反射
//通过方法的字符串形式实例化方法
SEL selector = NSSelectorFromString(@"setName:");
[object performSelector:selector withObject:@"a"];
//将方法变成字符串
NSString str = NSStringFromSelector(@selector(setName));
五、@synthesize和@dynamic分别有什么作用
-
@synthesize
的意义是,如果开发者没有手动实现setter
和getter
方法,那么编译器会自动为你加上这两个方法。默认就是@synthesize var = _var
。 -
@dynamic
告诉编译器,属性的setter
和getter
方法由用户自己实现,不自动生成。例如,一个属性被声明为@dynamic var
,然后用户没有手动实现setter
和getter
方法,编译的时候没有问题,但是当程序运行到instance.var = somevar
时,由于缺少setter方法会导致程序崩溃;或者运行到somevar = instance.var
时,由于缺少getter
方法同样会导致崩溃。
六、super关键字
下列代码中Student
继承自Person
,Person
继承自NSObject
。
#import "Student.h"
@implementation Student
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]); //Student
NSLog(@"[self superclass] = %@", [self superclass]); //Person
NSLog(@"[super class] = %@", [super class]); //Student
NSLog(@"[super superclass] = %@", [super superclass]); //Person
}
return self;
}
原理解析:
super
调用,底层会转换为objc_msgSendSuper2
函数的调用,_objc_msgSendSuper2函数内接收2个参数
struct objc_super2
和SEL``struct objc_super2
结构如下:
struct objc_super2 {
id receiver;
Class current_class;
};
receiver
是消息接收者,current_class
是receiver的Class对象,也就是当前类对象,objc_msgSendSuper2
函数会在内部获取当前类对象的父类,并且从父类开始查找方法。
七、isKindOfClass 与 isMemberOfClass
-
isMemberOfClass:
isKindOfClass:
对象方法底层实现
- (BOOL)isMemberOfClass:(Class)cls {
// 直接获取实例类对象并判断是否等于传入的类对象
return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
// 向上查询,如果找到父类对象等于传入的类对象则返回YES
// 直到基类还不相等则返回NO
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
-
isKindOfClass:
与isKindOfClass:
类方法底层实现
// 判断元类对象是否等于传入的元类元类对象
// 此时self是类对象 object_getClass((id)self)就是元类
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
// 向上查找,判断元类对象是否等于传入的元类对象
// 如果找到基类还不相等则返回NO
// 注意:这里会找到基类
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
由以上可知,下面代码打印结果:
NSLog(@"%d",[Person isKindOfClass: [Person class]]); //0
NSLog(@"%d",[Person isKindOfClass: object_getClass([Person class])]); //1
NSLog(@"%d",[Person isKindOfClass: [NSObject class]]);//1
1、类方法是获取元类对象与传入的参数进行比较,但是第一行我们传入的是类对象,因此返回NO。
2、同上,此时我们传入Person元类对象,此时返回YES。
3、由于基元类的superclass指针指向基类类对象,因此返回YES。
八、 为什么 Objective-C 对象存储在堆上而不是栈上
- 在Objective-C中,对象通常是指一块有特定布局的连续内存区域。
- 我们通常这样创建一个对象:
NSObject*obj=[[NSObjectalloc]init];
这行代码创建了一个NSObject
类型的指针obj
和一个NSObject
类型的对象,obj
指针存储在栈上,而其指向的对象则存储在堆上(简称为堆对象)。 - 目前 Objective-C 并不支持直接在栈上创建对象(简称为堆对象),但可以通过如下方式间接地创建:
struct { Class isa; } fakeNSObject; fakeNSObject.isa = [NSObject class]; NSObject *obj = (NSObject *)&fakeNSObject; NSLog(@"%@", [obj description]);
栈对象 obj 也能正常工作,由此可见栈对象和堆对象都是可行的,但为什么 Objective-C 不默认使用栈对象呢?
栈对象优缺点
1、优点
- 速度 :在栈上创建对象是非常快的,因为很多东西在编译时就确定了,运行时分配空间几乎不耗时;相对而言在堆上创建对象就非常耗时。
- 简单 :栈对象的生命周期是确定的,对象出栈以后就会被释放,不会存在内存泄漏,但这同时也是栈对象的最大缺点。
2、缺点
- 生命周期固定 :Objective-C 变量有效范围是由 “{}” 包含的块来决定的,也就是说栈对象的生命周期仅限于其所在的块里,出了块立马会被释放。一个对象被创建以后有可能会通过方法调用传递到别的方法,当栈对象的创建方法返回时,栈对象会被一起 pop 出栈而释放,导致其没法在别处被继续持有。此时 retain 操作会失效,除非用 copy 方法在想持有该栈对象的地方重新拷贝一份属于自己的栈对象。因此,栈对象回给对象的内存管理造成相当大的麻烦。
- 空间 :现代操作系统的栈和线程绑定,而栈空间是有限的,具体如下:
512 KB (secondary threads)
8 MB (OS X main thread)
1 MB (iOS main thread)
因此对象如果都在栈上创建不太现实,而堆只要物理内存不告警可以无限制使用。
综合以上优缺点,Objective-C 选择用堆存储对象。
九、id和instancetype的区别
1、instancetype 可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象。
2、在ARC环境下:instancetype用来在编译期确定实例的类型,而使用id的话,编译器不检查类型, 运行时检查类型。在MRC环境下:instancetype和id一样,不做具体类型检查。
//person类
@interface Person : NSObject
+(id)person;
@end
@implementation Person
+(id)person{
return [[self alloc]init];
}
@end
//Student类
@interface Student : Person
-(void)test;
@end
@implementation Student
-(void)test{
NSLog(@"---------");
}
@end
//在ARC环境下,遍历构造器方法创建的Person对象返回值是id类型,编译时不会检查类型,编译不会报错,但是当返回类型是instancetype的时候,编译器就会检查数据类型,发现Person类并没有test方法,因此在编译的时候就会报错。
[[Person person] test];
3、instancetype只能作为返回值,id可以作为参数。
十、项目中定位用的网络的还是GPS
iOS设备能提供3种不同途径进行定位:
1、 蜂窝式移动电话基站 (iOS优化:无网基站定位)
2、Wifi (iOS优化:无网WIFI定位)
3、GPS卫星 (iOS优化:A-GPS定位)
而iPhone的GPS与纯粹的GPS定位不同,称为A-GPS,即辅助GPS.(比GPS更优,不过GPS也分芯片和性能的,不是说所有的AGPS比所有的GPS都好)因为GPS定位中最耗时、最耗电的就是获取当前天上的卫星信息,哪些卫星可见、在什么位置、时钟是多少等等,这个过程可能花费数十秒甚至几分钟,而AGPS就是利用网络,首先将基站定位或者WIFI定位获得的大概位置发到远程服务器,有服务器进行查询和计算,得出这个位置下当前卫星信息,反馈给iOS设备,iOS设备就可以直接用这些信息来接受卫星信号,不用自己去扫描分析了。这样可以极大提高定位速度,将初次定位时间缩短到1~2秒完成。
A-GPS优点是定位快,缺点是需要网络,但也只是在初次定位时需要网络,因为一旦卫星信息返回,在有限时间和范围内,这些信息无须改变,之后的GPS定位就不再需要联网,都是直接用这些卫星参数接受信息了。
- iOS 不像Android系统在定位服务编程时,可以指定采用哪种途径进行定位。iOS的API把底层这些细节屏蔽掉了,开发人员和用户并不知道现在设备是采用 哪种方式进行定位的,iOS系统会根据设备的情况和周围的环境,采用一套最佳的解决方案。
- 在iOS设备上,上述定位方式会综合应用,一般地,可能先按照最快的“无网基站定位”返回一个位置,当有网络连接时,在用有网基站定位更新位置,然后,利用AGPS上网查询卫星星图,最后,在能收到GPS信号的情况下,转为使用GPS定位。
- 根据当前信号情况和网络环境,iOS可能在上述方式之间反复迭代,不一定一致特定步骤或者方式,而且随着iOS升级,定位顺序和规则可能改变。
参考文章:https://zhidao.baidu.com/question/1962374999608474260.html
十一栈和队列的区别,栈和堆的区别
栈和队列的区别:
- 栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
- 栈是先进后出,队列是先进先出。
- 栈只允许在表尾一端进行插入和删除,队列只允许在表尾一端进行插入,在表头一端进行删除。
栈和堆的区别:
- 栈区:由编辑器自动分配释放,存放函数的参数值,局部变量的值等(基本类型值)。
- 堆区:由程序员分配释放,若程序员不释放,程序结束时可能有OS回收(引用类型值)。
- 栈(数据结构):一种先进后出的数据结构。
- 堆(数据结构):堆可以被看成是一棵树,如:堆排序。
网友评论