1.#import和#include的区别以及 @class 相关作用?
-
import和#include都能完整地包含某个文件的内容,#import能防止同一个文件被包含多次
- @class仅仅是声明一个类名,并不会包含类的完整声明;@class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含
-
import <> 用来包含系统自带的文件,#import “”用来包含自定义的文件
- 详细说明
1.#import指令是Object-C针对#include的改进版本,#import确保引用的文件只会被引用一次,这样你就不会陷入递归包含的问题中
2.#import与@class二者的区别
在于:
- #import会链入该头文件的全部信息,包括实体变量和方法等;而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑。在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。
- 在编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。
- 如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.
2.用NSLog函数输出一个浮点类型,结果四舍五入,并保留一位小数
float num = 1.011;
NSLog(@"%.1f", num);
//使用%f来格式化,其中要保留一位小数,因此再用%.1f就是保留一位
3.property属性的修饰符有什么样的作用
property是属性访问声明,扩号内支持以下几个属性:
- readonly:是只读特性,只生成get方法的声明和实现,不会生成setter方法 ;不希望属性在类外改变
- readwrite:是可读可写特性,同时生成get方法和set方法的声明和实现
-
assign
:是赋值特性,set方法的实现是直接赋值,用于基本数据类型
,不进行任何retain操作,为了解决原类型与环循引用问题 -
retain
:表示持有特性,set方法的实现是release旧值,retain新值,传入参数的retaincount会+1;用于OC对象类型
-
copy
:表示拷贝特性,set方法的实现是release旧值,copy新值,用于NSString、block
等类型,这是为了减少对上下文的依赖而引入的机制 - nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic
- getter=getName、setter=setName:设置setter与getter的方法名
补充:
- retain 和copy用户对象,copy用于当 a指向一个对象,b也想指向同样的对象的时候,如果用assign,a如果释放,再 调用b会crash,如果用copy 的方式,a和b各自有自己的内存,就可以解决这个问题。retain 会使计数器加1,也可以解 决assign的问题。另外:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。 在多线程环境 下,原子操作是必要的,否则有可能引起错误的结果。
- strong:strong和retain相似,只要有一个strong指针指向对象,该对象就不会被销毁
- weak:声明为weak的指针,weak指针指向的对象一旦被释放,weak的指针都将被赋值为nil;
- unsafeunretained:用unsafeunretained声明的指针,指针指向的对象一旦被释放,这些指针将成为野指针。
4.写一个setter方法用于完成@property (nonatomic,retain)NSString *name,写一个setter方法用于完成@property(nonatomic,copy)NSString *name.
//@property (nonatomic, retain) NSString *name;
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
// @property(nonatomic, copy) NSString *name;
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
5.对于语句NSString*obj = [[NSData alloc] init]; ,编译时
和运行时
obj分别是什么类型?
- 在编译时,我们所声明的obj是NSString *类型,因此是NSString类型对象。
- 在运行时,由于指针obj所指向的是NSData类型对象的内存,因此实际上是NSData类型的对象。
- 说明:
在编译时,这一行代码会转换成类似这样:
NSString *obj = ((id (*)(id, SEL))objc_msgSend)([NSData class], @selector(alloc));
obj = ((id (*)(id, SEL))objc_msgSend)((id)obj, @selector(init));
由于在编译时,转换成id,因此可以用NSString *指向NSData对象,而id是具备运行时特性的,因此在链接时,通过id的isa指针可以找到其所属的类,因此最终类型还是通过isa确定其所属类型。
6.常见的object-c的数据类型有那些,和C的基本数据类型有什么区别?
- 常用OC类型:NSString、NSArray、NSDictionary、NSData、NSNumber等
- OC对象需要手动管理内存,C的基本数据类型不需要管理内存
6-1、OC中的数字对象都有哪些,简述它们与基本数据类型的区别是什么
- Objective-C中的数字对象NSNumber;
- Objective-C中的基本类型和C语言中的基本类型一样.主要有:int,long,float,double,char,void,bool等.
- 对于基本类型变量,不需要用指针,也不用手动回收,方法执行结束会自动回收.数字对象需要指针,也需要手动回收内存。
7. id声明的对象有什么特性?
id 声明的对象具有运行时
的特性,即可以指向任意类型的objcetive-c的对象;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
id类型是一个指向objc_object结构体类型的指针,这个结构体只有一个指向对象无类的指针isa,因此id可以指向任何类型的对象,故其具备运行时特性
8.Objective-C如何对内存管理的,说说你的看法和解决方法?
Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
- 每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁
- 通过retain可以让对象的计数器+1、release可以让对象的计数器-1
- 还可以通过autorelease pool管理内存
- 如果用ARC,编译器会自动生成管理内存的代码
9.内存管理的几条原则时什么?按照默认法则.哪些方法生成的对象需要手动释放?在和property结合的时候怎样有效的避免内存泄露?
- 黄金法则:
- 谁创建,谁release
➢ 如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease
➢ 换句话说,不是你创建的,就不用你去[auto]release
- 谁创建,谁release
- 谁retain,谁release
➢ 只要你调用了retain,无论这个对象是如何生成的,你都要调用release - 总结
➢ 有始有终,有加就有减
➢ 曾经让对象的计数器+1,就必须在最后让对象计数器-1- 只要调用了alloc、copy、new方法产生了一个新对象,都必须在最后调用一次release或者autorelease
- 只要调用了retain,都必须在最后调用一次release或者autorelease
- @property如果用了copy或者retian,就需要对不再使用的属性做一次release操作
- 如果用了ARC,另外讨论
10.看下面的程序,三次NSLog会输出什么?为什么?
NSMutableArray* ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"]; // 1
[str retain]; // 2
[ary addObject:str]; // 3
NSLog(@"%d", [str retainCount]);
[str retain]; // 4
[str release]; // 3
[str release]; // 2
NSLog(@"%d", [str retainCount]);
[ary removeAllObjects]; // 1
NSLog(@"%d", [str retainCount]);
结果:3、2、1
11.OC中创建线程
的方法是什么?如何指定在主线程
中执行代码?如何延时
执行代码?
1. 创建线程的方法
➢ NSThread
➢ NSOperationQueue和NSOperation
➢ GCD
2. 主线程中执行代码
➢ [self performSelectorOnMainThread: withObject: waitUntilDone:];
➢ [self performSelector: onThread:[NSThread mainThread] withObject: waitUntilDone:];
➢ dispatch_async(dispatch_get_main_queue(), ^{
});
➢ [NSOperationQueue mainQueue]addOperationWithBlock:^{
}
3. 延时执行
➢ double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
});
[self performSelector: withObject: afterDelay:];
[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:];
12.Object-C有多继承吗?没有的话用什么代替?
- OC是单继承,没有多继承
- 有时可以用分类和协议来代替多继承
注:多继承即一个子类可以有多个父类,它继承了多个父类的特 性。
Object-c的类没有多继承,只支持单继承,如果要实现多继承的话,可以通过类别和协议的方式来实现,OC类似于多继承,是在 用protocol委托代理来实现的;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一 般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
13.Object-C有私有方法吗?私有变量呢?
- OC没有类似@private的修饰词来修饰方法,只要写在.h文件中,就是公共方法
- 可以直接在.m文件中(比如类扩展)声明和实现方法,对编译器来说是私有的
- objective-c类里面的方法只有两种, 静态方法和实例方法.但是可以通过把方法的声明和定义都放在.m文件中来实现一个表 面上的私有方法。
- 有私有变量,可以通过@private来修饰,或者把声明放到.m文件中。在Objective‐C中,所有实例变 量默认都是私有的,所有实例方法默认都是公有的
14.关键字const
什么含义?
const int a;
int const a;
const int *a;
int const *a;
int * const a;
int const * const a;
- 前两个的作用是一样:a 是一个常整型数
- 第三、四个意味着 a 是一个指向常整型数的指针(整型数是不可修改的,但指针可以)
- 第五个的意思:a 是一个指向整型数的常指针(指针指向的整型数是可以修改的,但指针是不可修改的)
- 最后一个意味着:a 是一个指向常整型数的常指针(指针指向的整型数是不可修改的,同时指针也是不可修改的)
Const只是一个修饰符
,不管怎么样a仍然是一个int型的变量
本质:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
const关键字至少有下列n个作用
:
欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
15.static的作用?
- static修饰的函数是一个内部函数,只能在本文件中调用,其他文件不能调用
- static修饰的全部变量是一个内部变量,只能在本文件中使用,其他文件不能使用
- static修饰的局部变量只会初始化一次,并且在程序退出时才会回收内存
16.线程和进程的区别?
- 一个应用程序对应一个进程,一个进程帮助程序占据一块存储空间
- 要想在进程中执行任务,就必须开启线程,一条线程就代表一个任务
- 一个进程中允许开启多条线程,也就是同时执行多个任务
补充:
- 线程是进程的基本单位
- 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
- 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
- 进程有独立的地址空间,一个进程崩溃后,在保护模式下 不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
- 线程有自己的堆栈和局部变量,但线程之间没有单独的 地址空间,一个线程死掉就等于整个进程死掉,
- 多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较 大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。