1.Objective-C简介
1.1 简介
1.基于C语言,在C语言基础上,做了面向对象扩展。
2.1980年代初由 Brad Cox 和 Tom Love 发明,后来称为NeXT的主力语言,后被苹果收购,成为苹果开发平台的主力语言。
3.与Cocoa 和Cocoa Touch框架高度集成,支持开发Mac OS X、iOS应用。
-Mac OS X是苹果公司为Mac系列产品开发的专属操作系统。
-iOS是由苹果公司开发的手持设备操作系统。
4.在苹果开发平台上,通过LLVM编译器架构,支持与Swift语言双向互操作。
1.2 iOS开发平台
1.3开发方式
-Clang 或 GCC 命令行
a.clang -fobjc-arc Hello.m
b.-fobjc-arc 支持ARC内存管理
c.适合调试、研究
-Xcode项目
a.构建正规工程项目
b.使用大型框架,追求设计质量与代码组织
-Demo
#import
int main(int argc, const char * argv[]){
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
1>NSLog是一个日志输出函数,可以将传入的字符串参数输出到控制台。
2>@"Hello,World!"
是OC字符串,前面有@。
3>
#import
,包含某个文件的内容到 处理指令所在位置。
#import
表示包含Foundation框架中的Foundation.h文件。
OC使用
#import
包含头文件,可以自动防止同一个头文件被包含多次。
1.4 ObjC编译过程
2.类与对象
类(class)是一种表示对象类型的结构体。类有属性和方法。
对象(object)是类的具体化的东西,从抽象中到具体化出的特定个体。
OC一般用2个文件描述一个类(.h和.m)。
1> .h:类声明文件,用于声明成员变量、方法。类声明关键字@interface 和 @end。
RPoint.h
@interface RPoint: NSObject @property int x; @property int x; -(void) print; @end
2> .m:类的实现文件,用于实现.h中声明的方法。类实现关键字@implementation和@end。
RPoint.m
#import
#import "rpoint.h"
@implementation RPoint
-(void) print{
NSLog(@"[%d, %d]", self.x,self.y);
}
对象
创建对象
#import
#import "rpoint.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
RPoint* rp1=[[RPoint alloc] init];
}
return 0;
}
-->要用到Rpoint这个类,所以包含它的头文件#import "rpoint.h"
-->(1)调用Rpoint类的静态方法alloc分配存储空间,
RPoint* rp1=[[RPoint alloc] init];
返回Rpoint对象,用一个指向Rpoint类型的指针变量rp1来接收这个对象。用类名定义一个变量时,类名后面一定要带个*号。
alloc方法声明:+(id)alloc;
,返回值类型时id,id代表任何指针类型,可以代表任何OC对象。
-->(2)调用RPoint对象的构造方法init进行初始化
rp1 = [rp1 init];
类——引用类型
1>位于栈上的指针(引用)
2>位于堆上的实体对象
对象:栈上存储指针(引用),堆上存储真正的对象。
结构——值类型
实例直接位于栈中。
栈(stack)
1>无ARC负担,由系统自动管理,以执行函数为单位。
2>空间大小编译时确定(参数+局部变量)。
3>函数执行时,系统自动分配一个stack。
4>函数执行结束,系统立即自动回收stack。
5>函数之间通过拷贝值传递。
6>具有局部性,大小有限额,超出会stack overflow。
堆(heap)
1>分配由程序员手动请求(创建对象时)。
2>释放由运行时ARC机制自动释放(确定时)。
3>函数之间通过拷贝引用(指针)传递。
4>具有全局性,总体无大小限制(受制于系统内存整体大小)。
拷贝行为
RPoint* rp2 = rp1;
SPoint sp2 = sp1;
rp2.x++; rp2.y++;
sp2.x++; sp2.y++;
传参行为
process(rp1,sp1);
void process(RPoint* rp3, SPoint SP3){
rp3.x++;
rp3.y++;
sp3.x++;
sp3.y++;
}
3.数据成员:属性与实例变量
3.1 属性
属性表达实例状态,描述类型对外接口。相比较直接访问实例变量,属性可以做更多控制。
默认情况下,编译器会为属性定义propertyName自动合成:
1>一个getter访问器方法:propertyName
2>一个setter访问器方法:setPropertyName
3>一个实例变量 _propertyName
可以自定义访问器方法,也可以更改访问器方法名、或实例变量名。
可以使用静态全局变量(C语言)+类方法,模拟类型属性。
属性声明由@property 指令以及紧跟其后的特性组成。
@property NSString* firstName;
@property NSString* lastName;
@property int age;
@property NSDate* birthday;
@property (readonly) NSString* fullName;
3.2 实例变量
可以定义实例变量,而不定义属性。只有实例变量,没有类变量。
如果同时自定义了getter和setter访问器方法,或者针对只读属性定义了getter访问器方法,编译器将不再合成实例变量。
在类外一律使用属性来访问,类内大多也通过self使用属性访问。只有以下情况使用实例变量来访问:
1>初始化器 init
2>析构器 dealloc
3>自定义访问器方法
3.2 实例变量的生存周期
实例变量的存储:跟随对象实例存储在堆上。
值类型实例变量直接“内嵌”在对象实例中。跟随对象实例内存释放而被释放。
引用类型实例变量通过指针“引用”堆上的引用类型实例,ARC针对引用进行计数管理,自动释放引用计数为0的对象。
3.3 属性的描述特性(Attribute)
可以指定属性不同环境下的不同功能。
->读写特性:
readwrite或者readonly
指定该属性是否可写。默认是可读可写
@property (readonly) NSString* fullName;
->多线程特性:
nonatomic
指定生成的存取器函数是非原子性的,非线程安全;默认atomic
原子性,线程安全。
->内存管理特性
-->ARC环境
强引用strong(默认)
弱引用weak阻止循环引用
拷贝属性copy创建独立拷贝
-->其它情况
assign、retain、unsafe_unretained
决定为该属性生成的赋值函数的类型。
assign 简单地为变量赋值。retain 赋值到变量时会保留传入的参数。默认值是 assign
--循环引用(强引用)
WorkItem* workItem=[[WorkItem alloc] init];
workItem.content=@"CRM";
--弱引用
employee.workItem=workItem;
workItem.owner=employee;
--拷贝
a.赋值前
NSLog(@"Work Content: %@",workItem.content);
结果:Work Content: CRM
b.赋值后
NSMutableString *workContent = [NSMutableString stringWithString:@"ERP"];
workItem.content=workContent;
NSLog(@"Work Content: %@",workItem.content);
结果:Work Content: ERP
4.函数成员:方法
方法 Method
函数:代码段上的可执行指令序列
->全局函数(C语言函数)
->成员函数(ObjC方法)
方法是类的成员函数,表达实例行为或类型行为。
所有方法默认为公有方法。没有private或protected方法。
动态消息分发:方法调用通过运行时动态消息分发实现,在对象上调用方法又称“向对象发送消息”。
-(void) print;
-(BOOL) isEqualToPoint: (BLNPoint*) point;
+(BLNPoint*) getOriginPoint;
1.'-'表示动态方法(实例方法);'+'表示静态方法(类型方法)。
2.print前面的(void)表示方法无返回值,方法的返回值和参数类型都需要用小括号()包住。
3.OC方法中,一个冒号:对应一个参数。冒号 : 也是方法名的一部分。例如:isEqualToPoint:
4.方法调用:
BLNPoint* p1=[[BLNPoint alloc] init];
[p1 print];
实例方法或类型方法
1>实例方法——表达实例行为,可以访问
-->实例成员(实例属性、实例变量、实例方法)
-->类型方法、静态变量
2>类方法——表达类型行为,访问权限:
-->可以访问:类型方法、静态变量
-->不能访问:实例成员(实例属性、实例变量、实例方法)
3>了解编译器背后对实例方法和类方法的不同处理:self指针
对实例方法:实例对象的指针
对类方法:当前类的表示
方法参数
->如果参数类型为值类型,则为传值方式
-(void) moveToX:(int)x toY:(int)y;
如果参数类型为引用类型,则为传指针方式
-(BOOL) isEqualToPoint: (BLNPoint*) point;
->方法可以没有参数,也可以没有返回值
->如果方法有参数,方法名约定包含第一个参数名,从第二个参数开始需要显式提供外部参数名。
-(void) moveToX:(int)x toY:(int)y;
moveToX
包含第一个参数名,toY
为第二个参数的外部参数名。
->调用时,第一个参数名忽略,但后面的参数名必须显式标明。
[p1 moveToX:100 toY:200];
动态方法调用机制——消息分发
BLNPoint* origin=[BLNPoint getOriginPoint];
[origin print];
id obj=[[BLNPoint alloc] init];
[obj moveToX:50 toY:60];
[obj print];
//JMP obj-> methodLists-> &print
5.初始化器与析构器
认识
1.初始化器用于初始化对象实例或者类型,是一个特殊的函数。
->对象初始化器:-(id) init可以重载多个
-(id)init;
-(id)initWithName:(NSString *)name;
-(id)initWithName:(NSString *)name WithPages:(int)pages;
-(id)initWithName:(NSString *)name WithPages:(int)pages WithCategory:(NSString*)category;
->类型初始化器:+(void)initialize只能有一个
+(void)initialize;
2.析构器用于释放对象拥有的资源,无返回值的函数。
->对象析构器 -(void)dealloc 只有一个
->没有类型析构器
对象初始化器
->初始化对象实例时,init通常和alloc搭配使用。
->alloc所做的事情——NSObject已实现:
-->1.在堆上分配合适大小的内存。
-->2.将属性或者实例变量的内存置0。
->init所做的事情——可以自定义:
-->1.调用父类初始化器[super init](前置调用)。
-(id)init{
self = [super init];
if(self){
NSLog(@"Book Object init");
}
return self;
}
-->2.初始化当前对象实例变量(使用实例变量,不使用属性)。
-(id)initWithName:(NSString *)name WithPages:(int)pages WithCategory:(NSString*)category
{
self = [super init];
if (self) {
NSLog(@"Book Object init");
_name = [name copy];
_pages = pages;
_category = [category copy];
}
return self;
}
->new相当于调用alloc/init的无参数版本。
类初始化器
->类初始化器initialize负责类型级别的初始化。
->initialize在每个类使用之前被系统自动调用,且每个进程周期中,只被调用一次。
->子类的initialize会自动调用父类的initialize(前置调用)。
+(void)initialize
{
if(self ==[Book class]){
NSLog(@"Book Class initialize");
}
}
对象析构器
->对象析构器dealloc负责释放对象拥有的动态资源:
-->自动实现:1. ARC 将对象属性引用计数减持
-->手动实现:2.释放不受ARC管理的动态内存,如malloc分配的内存
-->手动实现:3.关闭非内存资源,如文件句柄、网络端口
-(void)dealloc
{
//1. 自动调用:ARC 对对象属性的引用技术减持
//2. 手工实现
NSLog(@"Book Object release");
//3. 自动调用:父类dealloc
}
->dealloc由ARC根据对象引用计数规则,在释放对象内存前自动调用,无法手工调用。
->子类的dealloc会自动调用父类的dealloc(后置调用)。
-->1.子类的某些对象实例继承自父类。需要调用父类的dealloc方法,来释放父类拥有的这些对象。
-->2.调用顺序:当子类的对象释放完时,再释放父类所拥有的实例。
6.继承
面向对象特性
->封装 encapsulation
隐藏对象内部实现细节,对外仅提供公共接口访问。
->继承 inheritance
一个类型在另外类型基础上进行的扩展实现。
->多态 polymorphism
不同类型针对同一行为接口的不同实现方式。
继承 Inheritance
->继承:每一个类只能有一个基类,子类自动继承基类的:
-->实例变量
-->属性
-->实例方法
-->类方法
Shape.h
#import
#import
@interface Shape : NSObject {
@public int _data;
}
@property int no;
-(void)draw;
-(void)move;
-(void)print;
+(void)process;
@end
Circle.h
#import "Shape.h"
#import
@interface Circle : Shape
@property int radius;
@end
子类 Circle继承基类Shape
Circle* circle = [[Circle alloc]init];
circle.no=200;//访问属性
circle->_data++;//访问实例变量
[circle draw];//访问实例方法
[circle print];//访问实例方法
[Circle process];//访问类方法
->所有类的根类:NSObject
->继承的两层含义:
-->成员复用:子类复用基类成员
*私有成员也被继承,子类访问不到。
-->类型抽象:将子类当作父类来使用(IS-A关系准则)
Circle is a Shape.
void process(Shape *shape){
shape.no++;
[shape draw];
}
process(circle);
7.多态
多态 Polymorphism
->多态:子类在父类统一行为接口下,表现不同的实现方式。
->对比重写与重载
-->子类重写父类同名同参数方法:子类只可以重写父类方法。
Rectangle继承Shape
@interface Rectangle : Shape
Rectangle重写Shape的方法:
#import "Rectangle.h"
@implementation Rectangle
//override 重写
-(id)init
{
self = [super init];
if (self) {
_length = 10;
_width = 20;
}
return self;
}
-(void)draw
{
NSLog(@"Rectangle object draw: length=%d, width=%d", self.length,self.width);
}
-(void)print
{
NSLog(@"Rectangle Instance variable %d", _data);
}
+(void)process
{
NSLog(@"Rectangle class process");
}
-(void)dealloc
{
NSLog(@"Rectangle dealloc");
}
@end
使用:
Rectangle *rect = [[Rectangle alloc]init];
rect.no=200;
rect->_data++;
[rect draw];
[rect print];
[Rectangle process];
[rect move];
----Shape是声明类型,Rectangle实际类型
Shape *rect = [[Rectangle alloc]init];
rect.no=200;
rect->_data++;
[rect draw];
[rect print];
[Rectangle process];
[rect move];
-->方法名相同、参数不同:OC不支持方法的重载。
->在子类的代码中,可以使用super来调用基类的实现。
-->self具有多态性,可以指向不同子类。
-->super没有多态性,仅指向当前父类。
Rectangle.m
-(int)no{
return super.no;
}
-(void)setNo:(int)no{
super.no=no;
}
Shape.m
{
[self draw];
}
继承中的init和dealloc
->初始化器 init
-->子类自动继承基类的初始化器
-->子类也可以重写基类初始化器,此时子类初始化器必须首先调用基类的一个初始化器(手工调用)。
->析构器 dealloc
-->子类可以选择继承基类析构器,或者重写基类析构器。
-->子类析构器执行完毕后,会自动调用基类析构器(后置调用,且不支持手工调用)。
-->子类析构器自动具有多态性。
->尽量避免在父类 init 和 dealloc 中调用子类重写的方法。