GeekBand~iOS~Objective-C~第一周

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开发平台
iOS开发平台.png
1.3开发方式

-Clang 或 GCC 命令行
a.clang -fobjc-arc Hello.m
b.-fobjc-arc 支持ARC内存管理
c.适合调试、研究
-Xcode项目
a.构建正规工程项目
b.使用大型框架,追求设计质量与代码组织
-Demo

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]){
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}

1>NSLog是一个日志输出函数,可以将传入的字符串参数输出到控制台。
2>@"Hello,World!"是OC字符串,前面有@。

输出结果.png

3>#import,包含某个文件的内容到 处理指令所在位置。
#import <Foundation/Foundation.h>
表示包含Foundation框架中的Foundation.h文件。
OC使用#import包含头文件,可以自动防止同一个头文件被包含多次。

1.4 ObjC编译过程
编译过程.png

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 <Foundation/Foundation.h>
#import "rpoint.h"
@implementation RPoint
-(void) print{
    NSLog(@"[%d, %d]", self.x,self.y);
}
对象

创建对象

#import <Foundation/Foundation.h>
#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>位于堆上的实体对象
对象:栈上存储指针(引用),堆上存储真正的对象。

对象存储.png

结构——值类型
实例直接位于栈中。

值的存储.png

栈(stack)
1>无ARC负担,由系统自动管理,以执行函数为单位。
2>空间大小编译时确定(参数+局部变量)。
3>函数执行时,系统自动分配一个stack。
4>函数执行结束,系统立即自动回收stack。
5>函数之间通过拷贝值传递。
6>具有局部性,大小有限额,超出会stack overflow。
堆(heap)
1>分配由程序员手动请求(创建对象时)。
2>释放由运行时ARC机制自动释放(确定时)。
3>函数之间通过拷贝引用(指针)传递。
4>具有全局性,总体无大小限制(受制于系统内存整体大小)。
拷贝行为

RPoint* rp2 = rp1;
SPoint sp2 = sp1;
拷贝后.png
rp2.x++;  rp2.y++;
sp2.x++;  sp2.y++;
更改后.png

传参行为

process(rp1,sp1);
void process(RPoint* rp3, SPoint SP3){
    rp3.x++;
    rp3.y++;
    sp3.x++;
    sp3.y++;
}
传参后.png
传参改变后.png

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 实例变量的生存周期

实例变量的存储:跟随对象实例存储在堆上。

对象实例变量.png

值类型实例变量直接“内嵌”在对象实例中。跟随对象实例内存释放而被释放。


值实例变量.png

引用类型实例变量通过指针“引用”堆上的引用类型实例,ARC针对引用进行计数管理,自动释放引用计数为0的对象。

3.3 属性的描述特性(Attribute)

可以指定属性不同环境下的不同功能。
->读写特性:
readwrite或者readonly
指定该属性是否可写。默认是可读可写
@property (readonly) NSString* fullName;
->多线程特性:
nonatomic指定生成的存取器函数是非原子性的,非线程安全;默认atomic原子性,线程安全。
->内存管理特性
-->ARC环境
强引用strong(默认)
弱引用weak阻止循环引用
拷贝属性copy创建独立拷贝
-->其它情况
assign、retain、unsafe_unretained
决定为该属性生成的赋值函数的类型。
assign 简单地为变量赋值。retain 赋值到变量时会保留传入的参数。默认值是 assign

强引用.png

--循环引用(强引用)

WorkItem* workItem=[[WorkItem alloc] init];
workItem.content=@"CRM";
循环引用.png

--弱引用

employee.workItem=workItem;
workItem.owner=employee;
弱引用.png

--拷贝

a.赋值前

NSLog(@"Work Content: %@",workItem.content);

结果:Work Content: CRM

赋值前结果.png

赋值前.png

b.赋值后

NSMutableString *workContent = [NSMutableString stringWithString:@"ERP"];
workItem.content=workContent;
NSLog(@"Work Content: %@",workItem.content);

结果:Work Content: ERP

赋值后结果.png

赋值后.png

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


消息分发表.png

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
}
内存释放.png

->dealloc由ARC根据对象引用计数规则,在释放对象内存前自动调用,无法手工调用。
->子类的dealloc会自动调用父类的dealloc(后置调用)。
-->1.子类的某些对象实例继承自父类。需要调用父类的dealloc方法,来释放父类拥有的这些对象。
-->2.调用顺序:当子类的对象释放完时,再释放父类所拥有的实例。

6.继承

面向对象特性

->封装 encapsulation
隐藏对象内部实现细节,对外仅提供公共接口访问。
->继承 inheritance
一个类型在另外类型基础上进行的扩展实现。
->多态 polymorphism
不同类型针对同一行为接口的不同实现方式。

继承 Inheritance

->继承:每一个类只能有一个基类,子类自动继承基类的:
-->实例变量
-->属性
-->实例方法
-->类方法
Shape.h

#import <objc/runtime.h>
#import <Foundation/Foundation.h>
@interface Shape : NSObject  {
    @public int _data;
}
@property int no;
-(void)draw;
-(void)move;
-(void)print;
+(void)process;
@end

Circle.h

#import "Shape.h"
#import <Foundation/Foundation.h>
@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

NSObject.png

->继承的两层含义:
-->成员复用:子类复用基类成员
*私有成员也被继承,子类访问不到。

-->类型抽象:将子类当作父类来使用(IS-A关系准则)
Circle is a Shape.

void process(Shape *shape){
    shape.no++;
    [shape draw]; 
}
process(circle);
继承的内存模型.png

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];
运行结果1.png

----Shape是声明类型,Rectangle实际类型

Shape *rect = [[Rectangle alloc]init];
        rect.no=200;
        rect->_data++;
        [rect draw]; 
        [rect print];
        [Rectangle process];
        [rect move];
运行结果2.png

-->方法名相同、参数不同:OC不支持方法的重载。

重载.png

->在子类的代码中,可以使用super来调用基类的实现。
-->self具有多态性,可以指向不同子类。
-->super没有多态性,仅指向当前父类。
Rectangle.m

-(int)no{
    return super.no;
}
-(void)setNo:(int)no{
    super.no=no;
}

Shape.m

{
    [self draw];
}
methodList的内存模型.png
继承中的init和dealloc

->初始化器 init
-->子类自动继承基类的初始化器
-->子类也可以重写基类初始化器,此时子类初始化器必须首先调用基类的一个初始化器(手工调用)。
->析构器 dealloc
-->子类可以选择继承基类析构器,或者重写基类析构器。
-->子类析构器执行完毕后,会自动调用基类析构器(后置调用,且不支持手工调用)。
-->子类析构器自动具有多态性。
->尽量避免在父类 init 和 dealloc 中调用子类重写的方法。

推荐阅读更多精彩内容