作者: | 来源:互联网 | 2023-09-23 16:53
1.iOS怎么管理内存?在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上)。如果一个对象创建并使用后没有得到及时释放那么就会占
1.iOS怎么管理内存?
在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型
是由系统自己管理的,放在栈上)。如果一个对象创建并使用后没有得到及时释
放那么就会占用大量内存。OjbC中的内存管理就需要由开发人员手动维护。
1)引用计数器:在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,
当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原
来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),
当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用
计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。
2)属性参数:可以通过@property定义属性,此刻不必手动实现
getter,setter方法程序仍然没有内存泄露,因为属性定义的时候我们同样加
上了参数
3)自动释放池:一种内存自动释放的机制叫做“自动引用计数”(或“自动释放
池”),使用@autoreleasepool关键字声明一个代码块,如果一个对象在初始
化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过
autorelease方法的对象都会自动调用一次release方法。这样对象就起到了
延迟自动释放的效果.
注意:
######1.autorelease方法不会改变对象的引用计数器,只是将这个对象放
到自动释放池中;
######2.自动释放池实质是当自动释放池销毁后调用对象的release方法,不
一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁);
######3.由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存
(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放
到多个自动释放池;
######4.ObjC中类库中的静态方法一般都不需要手动释放,内部已经调用了
autorelease方法;
2.浅复制和深复制的区别
浅复制:本质是将一个对象1的地址,交给了另一个对象2,如果对象被释放,则对
象2所指的地址是不安全的.
深复制:要使用copy方法,则对象必须遵守NSCopying协议
实质:将原对象的内容复制到一块新的内存空间,拷贝的是整个对象.
3.类别和类扩展的区别?
1)类扩展仅能够在原始类中声明(.h或.m中均可,在.m中声明的类扩展其定义的
属性和方法均是私有的)
2)类扩展的实现仅能够在原始类的.m中编写
3)在类扩展中可以扩展类的属性.而在分类中仅能够扩展实例方法和类方法
4.使用block和使用delegate完成委托模式有什么优点?
1)使用block实现委托模式优点
回调的block代码块定义在委托对象函数内部,使代码更为紧凑;适配对象不再
需要实现具体某个protocol,代码更为简洁。代码可读性更强,更有连贯性,
block经常可以用于completion handler、error handler等。网络请求回调。
2)delegate的优势:
1.非常严格的语法。所有将听到的事件必须是在delegate协议中有清晰的定义。
2.如果delegate中的一个方法没有实现那么就会出现编译警告/错误
3.协议必须在controller的作用域范围内定义
4.在一个应用中的控制流程是可跟踪的并且是可识别的;
5.在一个控制器中可以定义多个不同的协议,每个协议有不同的delegate
6.没有第三方对象要求保持/监视通信过程。
7.能够接收调用的协议方法的返回值。这意味着delegate能够提供反馈信息给controller
5.#import和#include的区别 @class?
@class一般用于头文件中需要声明该类的某个实例变量的时候用到,在m文 件中还是需要使用#import
而#import比起#include的好处就是不会引起交叉编译
6.在一个对象的方法里面:self.name = “object”;和name = “object”有什么不同吗?
self.name ="object":会调用对象的setName()方法;
name = "object":会直接把object赋值给当前对象的name属性。
7.请简要说明viewDidLoad和viewDidUnload何时调用
viewDidLoad在view从nib文件初始化时调用,
loadView在controller的view为nil时调用。
此方法在编程实现view时调用,view控制器默认会注册memory warning
notification,
当view controller的任何view没有用的时候,
viewDidUnload会被调用,在这里实现将retain的view release,如果是
retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责
release 。
8.数组和指针的区别
(1)数组可以申请在栈区和数据区;指针可以指向任意类型的内存块
(2)sizeof作用于数组时,得到的是数组所占的内存大小;作用于指针时,得
到的都是4个字节的大小
(3)数组名表示数组首地址,值不可以改变,如不可以将++作用于数组名上;
普通指针的值可以改变,如可将++作用于指针上
(4)用字符串初始化字符数组是将字符串的内容拷贝到字符数组中;用字符串初
始化字符指针是将字符串的首地址赋给指针,也就是指针指向了该数组
9.static的作用
(1)函数体内static 变量的作用范围为该函数体,不同于 auto 变量,该变
量的内存只被分配一次,
因此其值在下次调用时仍维持上次的值;
(2)在模块内的static 全局变量可以被模块内所用函数访问,但不能被模块外
其它函数访问;
(3)在模块内的static 函数只可被这一模块内的其它函数调用,这个函数的使
用范围被限制在声明
它的模块内;
(4)在类中的static 成员变量属于整个类所拥有,对类的所有对象只有一份拷
贝;
(5)在类中的static 成员函数属于整个类所拥有,这个函数不接收 this 指
针,因而只能访问类的static 成员变量。
10.简述内存分区情况
(1)代码区:存放函数二进制代码
(2)数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变
量、静态变量、常量
(3)堆区:通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释
放
(4)栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数
11.const char p; charconstp; charconst p; const char const p;四个修饰指针有什么区别
(1)定义了一个指向不可变的字符串的字符指针
(2)和(1)一样
(3)定义了一个指向字符串的指针,该指针值不可改变,即不可改变指向
(4)定义了一个指向不可变的字符串的字符指针,且该指针也不可改变指向
12.MVC的理解?
MVC模式考虑三种对象:模型对象、视图对象和控制器对象。
模型对象负责应用程序的数据和定义操作数据的逻辑;
视图对象知道如何显示应用程序的模型数据;
控制器对象是M与V之间的协调者。
13.obj-c的优缺点
objc优点:
1) Cateogies
2) Posing
3) 动态识别
4) 指标计算
5)弹性讯息传递
6) 不是一个过度复杂的 C 衍生语言
7) Objective-C 与 C++ 可混合编程
缺点: 1) 不支援命名空间
2) 不支持运算符重载
3) 不支持多重继承
4) 使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优
化方法都用不到。(如内联函数等),性能低劣。
14.队列和栈有什么区别:
队列和栈是两种不同的数据容器。从"数据结构"的角度看,它们都是线性结构,
即数据元素之间的关系相同。
队列是一种先进先出的数据结构,它在两端进行操作,一端进行入队列操作,一
端进行出列队操作。
栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操
作。
15.简述视图控件器的生命周期。
loadView尽管不直接调用该方法,如多手动创建自己的视图,那么应该覆盖这个
方法并将它们赋值给试图控制器的view属性。
viewDidLoad只有在视图控制器将其视图载入到内存之后才调用该方法,这是执
行任何其他初始化操作的入口。
viewDidUnload当试图控制器从内存释放自己的方法的时候调用,用于清楚那些
可能已经在试图控制器中创建的对象。
viewVillAppear当试图将要添加到窗口中并且还不可见的时候或者上层视图移
出图层后本视图变成顶级视图时调用该方法,用于执行诸如改变视图方向等的操
作。实现该方法时确保调用[super viewWillAppear:].
viewDidAppear当视图添加到窗口中以后或者上层视图移出图层后本视图变成顶
级视图时调用,用于放置那些需要在视图显示后执行的代码。确保调用[super
viewDidAppear:]。
16.UIView与CLayer有什么区别?
1) UIView是iOS系统中界面元素的基础,所有的界面元素都是继承自它。它本
身完全是由CoreAnimation来实现的。它真正的绘图部分,是由一个CALayer
类来管理。UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标
有关的属性。
2) UIView有个重要属性layer,可以返回它的主CALayer实例。
3) UIView的CALayer类似UIView的子View树形结构,也可以向它的layer上
添加子layer,来完成某些特殊的表示。即CALayer层是可以嵌套的。
4) UIView的layer树形在系统内部,被维护着三份copy。分别是逻辑树,这里
是代码可以操纵的;动画树,是一个中间层,系统就在这一层上更改属性,进行
各种渲染操作;显示树,其内容就是当前正被显示在屏幕上得内容。
5) 动画的运作:对UIView的subLayer(非主Layer)属性进行更改,系统将
自动进行动画生成,动画持续时间的缺省值似乎是0.5秒。
6) 坐标系统:CALayer的坐标系统比UIView多了一个anchorPoint属性,使
用CGPoint结构表示,值域是0~1,是个比例值。这个点是各种图形变换的坐标
原点,同时会更改layer的position的位置,它的缺省值是{0.5,0.5},即在
layer的中央。
7)渲染:当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以
调用setNeedsDisplay方法来重绘显示。
8)变换:要在一个层中添加一个3D或仿射变换,可以分别设置层的transform或
affineTransform属性。
9)变形:Quartz Core的渲染能力,使二维图像可以被自由操纵,就好像是三维
的。图像可以在一个三维坐标系中以任意角度被旋转,缩放和倾斜。
CATransform3D的一套方法提供了一些魔术般的变换效果。
17.线程与进程的区别和联系?
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元
实现系统对应用的并发性。
程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地
址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是
一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单
独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线
程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要
求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
18.ios平台怎么做数据的持久化?coredata和sqlite有无必然联系?coredata是一个关系型数据库吗?
iOS中可以有四种持久化数据的方式:属性列表、对象归档、SQLite3和Core
Data;core
data可以使你以图形界面的方式快速的定义app的数据模型,同时在你的代码中
容易获取到它。coredata提供了基础结构去处理常用的功能,例如保存,恢复,
撤销和重做,允许你在app中继续创建新的任务。在使用core
data的时候,你不用安装额外的数据库系统,因为core data使用内置的
sqlite数据库。core data将你app的模型层放入到一组定义在内存中的数据对
象。coredata会追踪这些对象的改变,同时可以根据需要做相反的改变,例如用
户执行撤销命令。当core
data在对你app数据的改变进行保存的时候,core data会把这些数据归档,并
永久性保存。
mac os x中sqlite库,它是一个轻量级功能强大的关系数据引擎,也很容易嵌
入到应用程序。可以在多个平台使用,sqlite是一个轻量级的嵌入式sql数据库
编程。与core
data框架不同的是,sqlite是使用程序式的,sql的主要的API来直接操作数据
表。
Core Data不是一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。虽
然Core Dta支持SQLite作为一种存储类型,但它不能使用任意的SQLite数据
库。Core
Data在使用的过程种自己创建这个数据库。Core Data支持对一、对多的关系。
19.tableView的重用机制?
UITableView通过重用单元格来达到节省内存的目的:通过为每个单元格指定一
个重用标识符(reuseIdentifier),即指定了单元格的种类,以及当单元格滚出
屏幕时,允许恢复单元格以便重用.对于不同种类的单元格使用不同的ID,对于简
单的表格,一个标识符就够了.
20.用变量a给出下面的定义
a) 一个整型
b) 一个指向整型数的指针
c) 一个指向指针的的指针,它指向的指针是指向一个整型数
d) 一个有10个整型数的数组
e) 一个有10个指针的数组,该指针是指向一个整型数的
f) 一个指向有10个整型数数组的指针
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
a) int a;
b) int *a;
c) int **a;
d) int a[10]
e) int *a[10];
f) int (*a)[10];
g) int (*a)(int);
i) int (*a[10])(int);