Objective-C
一门动态语言,动态两个字主要就体现在我们调用方法的时候,运行时回动态的查找方法,然后调用相应的函数地址。运行时是整个Objective-c程序的基石,有了它我们的程序才能正常运行起来。
NSObject是Cocoa中绝大部分类的基类,它主要是提供了序列话,拷贝对象,以及支持运行时动态识别的框架。
另外,在OC中,一切皆为指针。
在Objective-c中每一个类对象最开始的位置都会有一个isa指针,该指针指向一块内存区域,该部分主要包含两部分信息:
1、指向父类的指针。
2、自身的方法分发表。
当我们调用一个对象的某一个方法的时候,首先会在当前类的分发表中寻找该方法,如果找不到对应的方法,然后再去其父类中寻找该方法,依次类推直到找到对应的方法为止
流程图如下
你可能会想到,如果一个类有很深的继承层次,每次去调用根类的某个函数,岂不是都要做很多次查找。理论上是这个样子的,不过runtime也并非那么傻,它会为每一个类(不是对象)维护一个经常调用的方法的列表,只要调用过就会缓存起来(官方没有明确说明缓存机制),这样当程序运行稳定以后整个方法调用的过程就会更加高效。
本文不详细介绍OC的基本语法等内容:
OC基础数据类型:http://www.cnblogs.com/GISerYang/tag/Objective-C/
Cocoa框架
Cocoa提供了用于存放数字和字符串的通用数据类型的实际的类。非正式地可以将这些称为值类或基本值类。
Cocoa框架本身封装了三个独立的框架:Foundation基本框架、AppKit框架和核心数据框架
Foundation Framework
提供基本的构建块类,如字符串、数组、数值、文件访问等。可以在Mac和iPhone OS(iPhone、iPod touch和iPad上使用的系统)上使用此框架。Foundation类会使得更容易处理国际文本和数字,使得程序员从大量繁杂细节中解脱出来。
AppKit Framework
提供具体地处理用户接口元素(例如,窗口、控制和字体等)的框架。AppKit框架在Mac上的功能与iPhone OS上的UIKit功能相同,都是围绕相同的概念创建的。
Core Data Framework
提供数据存储、数据建模和自动改变操作轨迹(也称为自动回复或重做)。可以使用SQLite、XML、binary存储类型存放数据,或者甚至可以创建自己的存储类型。
非正式协议或类 @interface与@implementation到@end
@interface与@end间所做的声明,是约定要实现里面的方法,但是.m文件@implementation到@end间不实现也不会编译出错。
所以这种君子协定相对协议@protocol来说就是一种非正式协议。
换一种理解方式,interface和implementation共同代表一个类,即oc中的类必须包括两部分,interface部分和implementation部分;
将成员变量和成员方法的声明部分放置在interface部分中,包括继承关系,protocal实现关系,都在interface里面的头部进行声明;
实现部分放置在implementation部分中,相当于是将类拆分成声明和实现两部分,这两部分缺一不可。
@interface PropertyModel : NSObject{ NSString* sex; //无修饰,默认的protect属性只允许本类和子类访问. @public NSString* name; //所有类都能访问,访问方式为指针->. @private NSString* classes; //私有的只有本类能访问. } @property(nonatomic,copy)NSString* sexName; //通过self.sexName即可访问. @end
方法前都以‘-’减号开始(OC不区分public与private),相对如果是‘+’加号,表示是静态成员。
- (方法返回值的数据类型) 函数名: (参数1数据类型) 参数1的变量名 参数2的备注名字: (参数2数据类型) 参数2值的变量名 …. ;
注:可见OC方法除了第一个参数变量,每一个变量要有一个备注名字,调用语法与此类似。
公共属性 @property
一个类中的变量在@interface :NSObject{} 的括号中声明,而在 @interface : NSObject{}括号之后@end之前,也可以用 @property 去定义变量。
@interface中定义变量的话,你所定义的变量只能在当前的类中访问,在其他类中是访问不了的。
如果想方便的使用“self.变量名”的点访问方式去读写变量,并且类外也可以访问,那么变量就要被设置为类的属性。
@property 预编译指令的作用是自动在.h文件中声明属性的setter和getter方法。
@sythesize 创建了该属性的访问代码,编译器将添加实现 -setRainHandling:和-rainHandling方法的预编译代码。
@dynamic 当企图调用不存在的getter和setter方法,你将会得到一个错误。
格式:
@property (属性) 数据类型 *name;
在.h中用@property指明属性,在.m中用@synthesize表明生成相应的访问器。
属性的扩展
- assign //简单赋值,主要用于基本数据类型
- copy //创建一个新的对象,新的对象和旧对象是独立的两个对象
- retain //将对象计数器加1
- readonly //表示只读属性 只会生成getter方法 不会生成setter方法
- readwrite //默认值,表求生成setter和getter方法
- nonatomic //非原子访问,不加同步 ,多线程并发访问提高性能 (对多线程的保护,防止在未写完,被另一个线程读取,造成数据错误)
协议Protocol:用协议实现委托
相当于是C++中的纯虚基类,在写java的时候都会有接口interface这个概念,接口就是一堆方法的声明没有实现,而在OC里面,Interface是一个
类的头文件的声明,并不是真正意义上的接口的意思,在OC中,接口是由一个叫做协议的protocol来实现的。这个里面
可以声明一些方法,和java不同的是,它可以声明一些必须实现的方法和选择实现的方法。这个和java是完全不同的。
@optional和@required 可以理解为虚函数和纯虚函数。
委托本来的实现方法是,若B想让A做事,让B做老大则在B类中声明一个A的实例作为成员变量,这样B就可以调到A的方法;但是OC中采用另一种反向思维模式,B想委托A做事让A做老大,在A中实例B,再设置实例B的一个指针指向A自己,B中用这个指针来调用A的函数,而这些被需要的函数就是通过B的协议让A遵守来实现。
MyProtocol.h来声明协议接口:只声明方法,不做具体实现
#import <Foundation/Foundation.h> @protocol MyProtocol <NSObject> @optional //这个方法是可选的 - (void) print:(int) value; @required //这个方法是必须实现的 - (int) printValue:(int) value1 andValue:(int) value2; @end
则引用MyProtocol协议的类必须按要求实现协议的方法
#import <Foundation/Foundation.h> #import "MyProtocol.h" @interface MyTest : NSObject <MyProtocol> - (void) showInfo; @end
下面举个例子:
当A view 里面触发打开B view(B是A的一个模态视图),而B view需要反馈修改A view的界面,那么这个时候B就需要用到委托了。
在B中声明一个与委托人的协议,和一个委托人指针。某类遵守了这个协议,就要按要求实现这些函数,实现A的实例并将B的委托人指针指向自己,供B来通过指针调用。
在B中可以通过函数 [委托人指针 respondsToSelector:@selector(函数名:)]来检查委托人是否实现了某个函数。
A_View.h: @interface:UIViewController<B_ViewDelegate> { B_View *myB_View; } @end A_View.m: - (void)viewWillAppear:(BOOL)animated { myB_View.touchDelegate = self; //将委托 ,这里可以看出委托其实也可以理解为B_View中的一个属性 [self.view addSubview: myB_View]; } - (void)ontouch:(id)sender { //实现协议B_ViewDelegate的ontouch方法供B调用修改A }
B_View.h: @protocol B_ViewDelegate<NSObject> @optional -(void)touch:(id)sender; @end @interface B_View:UIViewController {} @property(nonatomic,retain) id<B_ViewDelegate> touchDelegate; //委托人指针 @end B_View.m: @synthesize touchDelegate; - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [touchDelegate touch];//调用协议委托,委托给A_View来实现函数 } return self; } @end
类目Category
封装是面向对象的一个特征,oc也不例外,但是有的时候我们会碰到一种情况,比如我们封装了一个类,不想再动它了,但是我们又需要在那个类中增加一个方法,这时候我们就不必在那个类中做修改或者再定义一个它的子类,只需要添加一个类目(Category)即可。建立oc的runtime机制下的,Java,C++都是有runtime机制的语言。——runtime机制简单来说就是在编译时不确定对象类型,在运行时才确定。Category提供了一种比继承(inheritance)更为简洁的方法来对class进行扩展,无需创建对象类的子类就能为现有的类添加新方法,可以为任何已经存在的class添加方法,包括那些没有源代码的类(如某些框架类)。
类别主要有3个作用:
(1)可以将类的实现分散到多个不同文件或多个不同框架中,方便代码管理。也可以对框架提供类的扩展。
(2)创建对私有方法的前向引用:如果其他类中的方法未实现,在你访问其他类的私有方法时编译器报错这时使用类别,在类别中声明这些方法(不必提供方法实现),编译器就不会再产生警告
(3)向对象添加非正式协议:创建一个NSObject的类别称为“创建一个非正式协议”,因为可以作为任何类的委托对象使用。
例如:给NSString添加一个NewFunc方法,功能特点为MyCompare作为描述类目用途的唯一名字,体现按类别分开实现的思想。
类目名 NSString+MyCompare.h
#import <Foundation/Foundation.h> @interface NSString (MyCompare) -(void)test; @end
类目名 NSString+MyCompare.m
#import "NSString+MyCompare.h" @implementation NSString (MyCompare) -(void)NewFunc{ } @end
例如:AsyncSocket这个iOS Socket插件中的.h文件就是采用类目的方式想NSObject添加了相关的Socket功能,减少了插件采用协议方式造成的开发麻烦。
@interface NSObject (AsyncUdpSocketDelegate) - (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag; - (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error; - (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port; - (void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error; - (void)onUdpSocketDidClose:(AsyncUdpSocket *)sock; @end
iOS视图控制
一、UIWindow是一种特殊的UIView,通常在一个程序中只会有一个UIWindow,但可以手动创建多个UIWindow,同时加到程序里面。UIWindow在程序中主要起到三个作用:
1、作为容器,包含app所要显示的所有视图
2、传递触摸消息到程序中view和其他对象
3、与UIViewController协同工作,方便完成设备方向旋转的支持
二、通常我们可以采取两种方法将view添加到UIWindow中:
1、addSubview
直接将view通过addSubview方式添加到window中,程序负责维护view的生命周期以及刷新,但是并不会为去理会view对应的ViewController,因此采用这种方法将view添加到window以后,我们还要保持view对应的ViewController的有效性,不能过早释放。
2、rootViewController
rootViewController时UIWindow的一个遍历方法,通过设置该属性为要添加view对应的ViewController,UIWindow将会自动将其view添加到当前window中,同时负责ViewController和view的生命周期的维护,防止其过早释放
三、WindowLevel
UIWindow在显示的时候会根据UIWindowLevel进行排序的,即Level高的将排在所有Level比他低的层级的前面。级别的高低顺序从小到大为Normal < StatusBar < Alert,这说明当Level层级相同的时候,只有第一个设置为KeyWindow的显示出来,后面同级的再设置KeyWindow也不会显示。(keyWindow是指定的用来接收键盘以及非触摸类的消息,而且程序中每一个时刻只能有一个window是keyWindow)
四、UIViewControl 管理程序中的众多视图
view最好在需要显示时再去加载,并且在系统发出内存警告时释放必要的view及相关的数据对象。
1、初始化时会根据需要调用init,initWithCoder等相关函数,这个时候我们可以做一下简单的初始化操作,建立ViewController中需要使用的数据模型等
不建议在初始化阶段就直接创建view及其他与显示有关的对象(应该放到loadView的时候去创建,或者采用懒加载的方法创建)ViewController可以通过代码和xib两种方式创建,这两种方式的初始化流程也不尽相同。
1)使用xib创建的VC
xib其实最终是会把我们的设置保存成一个数据集,当需要初始化构建VC的时候,回去读取记录的数据集,然后帮我们动态的创建VC,因此可以想象它在初始化时会先去找看是否实现initWithCoder方法,如果该类实现了该方法,就直接调用initWithCoder方法创建对象,如果没有实现的话就调用init方法。调用完初始化方法以后紧接着会调用awakeFromNib方法,在这个方法里面我们可以做进一步的初始化操作。
2)使用代码创建VC
使用代码创建时,我们根据需要手动的创建VC中的数据,如果自己定制VC时,还需要在init中调用[super init]。
2、UIViewController中View的load和unload生命周期
前面讲了不建议在VC初始化的时候就创建view及其他与显示相关的代码,官方文档建议将View的初始化操作放到loadView的时候再做,当VC接到内存告警时会调用didRecieveMemoryWarning这个时候我们就要做出响应,释放暂时不需要的对象。如果无视这个警告,系统内存不够用时会会继续发送,如果还得不到处理就会强制退出程序。下面看具体的loadView和unloadView时候都会做什么操作。
1)Load周期
当需要显示或者访问view属性时,view没有创建的话,VC就会调用loadView方法,在这个时候会创建一个view并将其赋给VC.view属性。紧接着就会调用VC的viewDidLoad方法,这个时候VC.view保证是有值的,可以做进一步的初始化操作,例如添加一些subview。注意:定制VC时,如果覆盖loadView方法,不需要调用[super loadView]方法。
2)Unload周期
当app收到内存警告的时候,会调用每一个VC的didRecieveMemoryWarning方法,我们需要做出响应,释放程序中暂时不需要的资源。通常都会重写该方法,重写时候需要调用super的该方法。如果检测到当前VC的view可以被安全释放的话,就会调用viewWillUnload方法,当VC的view消失时候它的subviews可能会被一起释放。调用viewWillUnload以后,会将VC.view属性设置成nil,然后在调用viewDidUnload方法,这个时候我们可以释放那些强引用的对象。
iOS开发入门知识点总结