iOS开发--Objective-C语言高级部分

    xiaoxiao2022-07-02  129

    1.Objective-C代码的文件扩展名

    当你需要在源代码中包含头文件的时候,你可以使用标准的 #include 编译选项,但是 Objective-C 提供了更好的方法。#import 选项和 #include 选项完全相同,只是它可以确保相同的文件只会被包含一次。Objective-C 的例子和文档都倾向于使用 #import

    2.Objective-C类和对象

    Objective-C编程语言的主要目的是为C编程语言添加面向对象,类是Objective-C的核心特性,支持面向对象编程,通常称为用户定义类型。

    类是面向对象程序设计( OOP )的构建块。实际上,通过OOP编写的程序主要由相互作用的类实例(即对象)的网络构成。objective-C语言对OOP提供了全面的支持,其中包括设计时的类规范和运行时的类实例创建等语言特性,以及支持对象进行交互的多种机制。

    例:新建一个类 HelloWorld类 系统会帮我们生成HelloWorld.h HelloWorld.m两个文件

    面向对象编程是一种计算机程序设计方式,其重点是创建和使用软件对象编写程序。软件对象提供了被建模事物/概念的特点或属性的一种表示形式(它们的状态),以及它们可以做的事情的定义(它们的方法)。在使用Objective-C时, 可以为拥有类接口和相应实现的对象,创建规范或蓝图。

    .h文件写类的接口,接口中可以设置类的结构(即它的属性和方法)。 .m文件写类的实现,在实现时可以设置存储类的内部状态的变量,还可以通过定义类的属性和方法实现其逻辑。

    (1)Objective-C特征

    类定义在两个不同的部分,即@interface和@implementation。几乎所有东西都是对象的形式。对象接收消息,对象通常称为接收者。对象包含实例变量。对象和实例变量具有范围。类隐藏对象的实现。属性用于提供用于其他类对此类实例变量的访问。

    (2)Objective-C类定义

    Objective-C 的类规格说明包含了两个部分:定义(interface)与实现(implementation)

    interface

    定义 interface部分 包含了类声明和实例变量的定义,以及类相关的方法。

    清楚定义了类的名称、数据成员和方法。 以关键字@interface作为开始,@end作为结束。

    @interface MyObject : NSObject { int memberVar1; // 实体变量 id memberVar2; } +(return_type) class_method; // 类方法 -(return_type) instance_method1; // 实例方法

    Implementation 实现implementation 部分包含了类方法的实际代码。

    实现区块则包含了公开方法的实现,以及定义私有(private)变量及方法。 以关键字@implementation作为区块起头,@end结尾。

    @implementation MyObject { int memberVar3; } +(return_type) class_method { .... //method implementation } -(return_type) instance_method1 { .... } @end

    (3)构造一个类 构造方法: 用于在对象创建出来的时候为对象的成员变量或者属性赋值 实现方式:

    h文件存放类的申明 @interface开头 @end结尾    m文件存放类的实现 @implementation开头OC所有类必须继承基类NSObject,无多继承支持协议,支持多态”-“对象方法 “+”类方法

    例如:创建一个类MGPerson MGPerson.h文件 #import <Foundation/Foundation.h> @interface MGPerson : NSObject @end

    MGPerson.m文件 #import “MGPerson.h” @implementation MGPerson @end

    下面我们在类中写一些方法 Person.h文件

    #import <Foundation/Foundation.h> @interface MGPerson : NSObject { //成员变量 @public //全局变量 int age; @private //私有变量 自己使用 float height; @protected //受保护 子类和自己使用 NSString *name; } //构造方法 //以init开头一般都是构造方法 返回值为id类型 也可以写成(Person *) - (id)init; - (id)initWithAge:(int)newAge; - (id)initWithAge:(int)newAge andHeight:(float)newHeight; //set get方法 - (void)setAge:(int)newAge; - (void)setHeight:(float)newHeight; - (int)getAge; - (float)getHeight; @end

    Person.m文件

    #import "MGPerson.h" @implementation MGPerson { //可以在这里声明成员变量 但只能自己用 } //方法的实现 - (id)init { //super 表示父类 表示NSObject super init表示父类调用自己的init方法 //(1)分配内存 (2)内存空间指向self self表示对象本身 if (self = [super init]) { age = 25; height = 172; name = @"Tom Siebel"; } return self; } - (id)initWithAge:(int)newAge { if (self = [super init]) { newAge = 25; height = 172; name = @"Tom Siebel"; } return self; } - (id)initWithAge:(int)newAge andHeight:(float)newHeight { if (self = [super init]) { newAge = 25; newHeight = 172; name = @"Tom Siebel"; } return self; } //set get方法的实现 - (void)setAge:(int)newAge { age = newAge; } - (void)setHeight:(float)newHeight { height = newHeight; } -(int)getAge { return age; } - (float)getHeight { return height; } @end

    注:OC中的init方法的作用是在对象实例化后对其初始化

    set和get方法:用来对数据进行属性赋值和读取属性值

    我们在OC中定义一个类的时候,它的成员变量默认情况下是@protected的。这是因为我们的成员变量一般不允许直接去访问,如果需要修改和访问成员变量我们就要用到成员变量的set方法和get方法。set方法是用来给成员变量进行赋值的,也可以起到对传入的数据进行过滤的作用。

    为什么使用set和get方法? 这是因为我们的成员变量要进行封装,不能让外部直接对变量进行修改和访问。这提高了安全性的同时也可以让我们不需要去关注实现的细节。

    set方法作用:提供一个方法给外界设置成员变量的值,有一定的安全性 set方法的命名规范:    方法法必须以set开头,后面跟上成员变量的名称,并且成员变量的名称首字母大写    一定要接收一个参数,参数类型跟成员变量的数据类型一致    定义的形参名称不能跟成员变量名称一样    返回值一定是void

    get方法的作用:返回对象内部的成员变量的值 get方法的命名规范:    返回值类型必须和成员变量的数据类型一致    方法名称一般跟成员名称一样    没有形参

    (4)对象----创建对象

    Objective-C创建对象需通过alloc以及init两个消息。alloc的作用是分配内存,init则是初始化对象。 init与alloc都是定义在NSObject里的方法,父对象收到这两个信息并做出正确回应后,新对象才创建完毕。

    MyObject * my = [[MyObject alloc] init];

    在Objective-C 2.0里,若创建对象不需要参数,则可直接使用new

    MyObject * my = [MyObject new];

    注:new与alloc/init的区别

    new里的代码

    + new { id newObject = (*_alloc)((Class)self, 0); Class metaClass = self->isa; if (class_getVersion(metaClass) > 1) return [newObject init]; else return newObject; }

    而 alloc/init 里的代码

    + alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); } - init { return self; }

    源码中我们发现,[className new]基本等同于[[className alloc] init],区别只在于alloc分配内存的时候使用了zone。zone是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度。

    为什么不推荐使用new?

    不知大家发现了没有,如果使用new的话,初始化方法被固定死只能调用init。而你想调用initXXX怎么办?没门儿!据说最初的设计是完全借鉴Smalltalk语法来的。传说那个时候已经有allocFromZone:这个方法,但是这个方法需要传个参数id 这个方法只是给对象分配了内存,并没有初始化实例变量。 概括来说,new和alloc/init在功能上几乎是一致的,分配内存并完成初始化。差别在于,采用new的方式只能采用默认的init方法完成初始化,采用alloc的方式可以用其他定制的初始化方法。

    (5)实例变量

    实例变量是指为类声明的变量,它们在相应类实例(即对象)的生命周期中存在并拥有值。当对象被创建时,系统会为实例变量分配内存,当对象被释放时系统也会释放变量占用的内存。实例变量拥有与对象对应的作用范围和命名空间。Objective-C提供了控制实例变量直接访问方式的特性,还提供了获取和设置它们的值的方便机制。

    访问对象的实例变量的方式属于对象功能范畴。在对象内部,对象的任何实例方法都可以访问该对象的实例变量。通过外部类实例直接访问对象的实例变量属于变量功能范畴。Objective-C拥有多条编译器指令,专门用于设置实例变量的作用范围(即访问控制)。

    @private:将实例变量设置为只能在声明它的类以及与该类类型相同的其他实例中访问。 @protected:将实例变量设置为只能在声明它的类及其子类的实例方法中被访问。在没有明确设置实例变量的访问控制等级时,这是实例变量默认的作用范围。 @public:将实例变量设置为可以被任何代码访问。 @package:将实例变量设置为可以被其他类实例和函数访问,但是在其所属程序包的外部,它会被视为私有变量。这种作用范围可以用于库或框架类。

    实例变量可以在类的接口或实现部分中声明,不过,在类的公有接口中声明实例变量会违反OOP的关键宗旨之一一封装。 因此,最好在类的实现部分中声明实例变量,而当语句块紧跟类的@implementation指令时,尤应如此。

    (6)属性

    使用关键字@property后跟-组可选的特性(用圆括号括起来的)、属性的类型和名称,可以声明属性:

    @property (特性) 属性的类型 属性的名称;

    属性的特性

    注:

    (1)ARC 自动内存管理 是编译器特性,而不是 iOS 运行时特性

    ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。

    (2)strong和weak

    strong指针能够保持对象的生命,一个对象只要有strong指针指向它,那么它就不会被释放

    weak型的指针变量仍然可以指向一个对象,但不属于对象的拥有者。即当对象被销毁的时候,这个weak指针也就自动指向nil(空指针)。

    举例: 小马哥的解释

    输入框UITextField 输入文字后给name赋值

    NSString *name1 = self.nameField.text;(strong) __weak NSString *name2 = self.nameField.text;(weak)

    当我们改变UITextField内容 name1仍然存在,然而name2此时为nil

    (3)copy和retain的引用计数

    copy : 建立一个索引计数为 1 的对象,然后释放旧对象 retain :释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为 1 Copy 其实是建立了一个相同的对象,而 retain 不是 比如一个 NSString 对象,地址为 0×1111 ,内容为 @”STR” Copy 到另外一个 NSString 之后,地址为 0×2222 ,内容相同,新的对象 retain 为 1 ,旧有对象没有变化 retain 到另外一个 NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的 retain 值 +1 也就是说, retain 是指针拷贝, copy 是内容拷贝。 NSString 类举例是错误的。NSString没有retain,定义时静态分配一段内存。

    属性的定义 属性定义中会包含getter和setter方法

    @property(nonatomic,strong)NSString *name 包含 - (void)setName:(NSString *)name{ _name = name; } - (int)name { return _name; }

    关键字补全@synthesize ,编译器自动生成属性定义 @synthesize name 编译器会通过 @synthesize自动生成,从而创建_name @dynamic属性指令可以阻止编译器自动生成getter和setter方法

    @implementation Person @synthesize name; @dynamic age;//关键字表明访问方法会由程序员手工提供 -(id)initWithAge:(int)initAge { age = initAge; // 注意:直接赋给成员变量,而非属性 return self; } -(int)age { return 29; // 注意:并非返回真正的年龄 } @end

    访问属性:访问器方法和点语法

    访问器方法 getter方法:用于读取值,拥有和属性相同的名称 setter方法:用于设置值,其名称已set开头后跟首位大写的属性名

    点语法 mgObject.属性的名称 获取属性的值 mgObject.属性的名称 = 输入值 设置属性的值

    (6)方法

    Objective-C 中的类可以声明两种类型的方法:实例方法和类方法。实例方法就是一个方法,它在类的一个具体实例的范围内执行。也就是说,在你调用一个实例方法前,你必须首先创建类的一个实例。而类方法,比较起来,也就是说,不需要你创建一个实例。

    方法的声明由方法类型,返回值类型和一个或多个方法代码段(提供名称,参数和参数类型构成

    +(id)messageWithName:(NSString *)name age:(int)age

    方法的类型标识符表明了该方法是类方法还是实例方法。

    +(加号)表示,这表示该方法拥有类的作用范围,这意味着它使用类级的操作并且无法访问类的实例变量(除非这些变量被当做参数传给它)。

    -(减号)表示,这表明该方法拥有对象的作用范围,这意味着它使用实例级的操作,并且可以直接访问对象及其父对象的实例变量(根据实例变量上设定的访问控制)。

    +是类方法 调用方式:(类 函数) 静态方法在堆上分配内存。

    -是对象方法,实例方法 调用方式:(实例对象 函数) 实例方法在堆栈上。

    3.面向三个基本特征(继承、封装、多态)

    面向过程

    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。 ——执行者

    面向对象

    面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。——指挥者 例(1):把大象装冰箱有几步?? 面向过程:把冰箱打开-把大象放进去-关门 面向对象:创建冰箱对象-冰箱.打开-冰箱.存放-冰箱.关门 例(2):五子棋 面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。 面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

    面向对象的三个基本特征:继承、封装、多态

    继承

    面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

    子类可以直接复用父类中的成员。子类继承父类所有方法的声明和实现,非私有的实例变量以及协议,继承是要在 .h 文件中声明一下。继承具有单根性和传递性。

    通过继承创建的新类称为“子类”或“派生类”。 被继承的类称为“基类”、“父类”或“超类”。

    继承的好处:

    抽取出了重复的代码;建立了类和类之间的联系; 缺点: 耦合性太强

    注:

    OC语言是单继承语言。在oc语言中,基本上所有类的根类都是NSObject类。编译器从上往下执行,所以在子类前面至少应该要有父类的声明;oc中不允许子类和父类拥有相同名称的成员变量名;oc中的子类可以拥有和父类相同名称的方法,在子类调用时,优先去自己的内部寻找,如果没有则一层一层往上找; Tip:重写就是子类重新实现了父类中的某个方法,覆盖了父类以前的实现。

    封装

    封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。 封装就是定义类,定义属性,定义方法; 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

    多态

    多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

    多态在代码中的体现,就是多种形态,必须要有继承,没有继承就没有多态;在使用多态时,会进行动态监测,以调用真实的对象方法;多态在代码中的体现即父类指针指向子类对象;不同对象对同一消息的不同相应,子类可以重写父类的方法,多态就是允许方法重名 参数或返回值可以使父类行传入或返回。

    4.Category类别、Posing冒充、扩展

    (1)Category类别

    有时,可能会发现希望通过添加仅在某些情况下有用的行为来扩展现有类。 要向现有类添加此类扩展,Objective-C提供了类别和扩展。 类别:类别是一种为现有的类添加新方法的方式。使用类别你可以为任何类添加新的方法,包括那些没有源代码的类。 通常会以“类名称+类别名称”的风格命名。这并不是硬性的规定,但却是一个好习惯。例如:

    NSString+NumberAsNSNumber.

    类别的声明

    #import <Foundation/Foundation.h> @interface NSString (NumberConvenience) -(NSNumber *) lenthAsNumber; @end

    声明部分有几个非常有趣的特点。

    类的名称后面是位于括号中的新名称,这意味着类别的名称叫做NumberConvenience,而且它是添加给NSString类的。只要保证类别名称唯一,你可以向一个类中添加任意数量的类别。你可以指定想要添加类别的类(上述是NSString)和类别的名称(上述是NumberConvenience),还可以列出你要添加的方法,最后以@end结束。由于不能添加新的实例变量,因此这是不会像类声明那样包含实例变量声明的部分。你可以在类别中添加属性,但是不能添加实例变量,而且属性必须是@dynami类型的。

    类别的实现

    #import "NSString+NumberConvenience.h" @implementation NSString (NumberConvenience) -(NSNumber *) lenthAsNumber { //获取客串的长度值 NSUInteger length =[self length]; return ([NSNumber numberWithUnsignedInt:length]); } @end

    类别的调用

    #import <Foundation/Foundation.h> #import "NSString+NumberConvenience.h" int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableDictionary *dict =[NSMutableDictionary dictionary]; [dict setObject:[@"hello" lenthAsNumber] forKey:@"hello"]; [dict setObject:[@"once time" lenthAsNumber] forKey:@"once time"]; NSLog(@"%@",dict); } return 0; }

    类别的优势与缺陷

    优点: 将类的实现代码分散到多个不同的文件或框架中。 创建对私有方法的前向引用。 向对象添加非正式协议。

    缺陷: 类别无法向类中添加新的实例变量。类别没有空间容纳实例变量。 名称冲突,也就是类别中的方法与现有的方法重名。当发生名称冲突时,类别具有更高的优先级。你的类别方法将完全取代初始方法,导致初始方法不再可用。

    类别和继承 为现有的类添加新的行为,通常也可以采用创建子类的方法(继承), 但是类别是有限制的,如现有的类你没有源代码,或者现有的类是类簇的形式存在。都无法添加新的行为。

    (2)Posing冒充 跟Category类别类似,但本质上不一样,Posing存在的目的在于子类可以冒充父类,使得后续的代码无需把父类修改为子类,就可以很方便的让父类表现成子类的行为,从而实现非常方便的冒充

    在Mac OS X 10.5中声明已经弃用了冒充(Posing) 这里不再多做介绍

    (3)类扩展 类扩展与类别有一些相似之处,但它只能添加到编译时具有源代码的类中(类与类扩展同时编译)。 类扩展声明的方法是在原始类的实现块中实现的,因此不能在框架类上声明类扩展,例如Cocoa或Cocoa Touch类 扩展名实际上是没有类别名称的类别,它通常被称为匿名类别。 声明扩展的语法使用@interface关键字,就像标准的Objective-C类描述一样,但不表示子类的任何继承。 它只是添加括号,如下所示 -

    @interface ClassName () @end

    扩展的特征

    不能为任何类声明扩展,仅适用于原始实现源代码的类。扩展是添加仅特定于类的私有方法和私有变量。扩展内部声明的任何方法或变量即使对于继承的类也是不可访问的。

    5.协议(代理delegate)

    协议protocol 使用协议声明的方法和属性可以由任何类实现。 类接口直接与特定的类关联,因此也会直接与类的层次结构关联。相对而言,协议不与特定的类关联,因此使用它可以捕捉无继承关系的类之间的相似之处。协议使Objective-C支持多重继承规范的概念(方法声明)。协议还可以用于定义对象能够发送哪些消息(通过设置遵守协议的属性)

    @protocol 协议名称 @required @optional

    //属性声明 //方法声明 @end

    协议的声明以@protocol指令开头,后跟协议的名称,以@end指令结束。 协议拥有必选方法和可选方法;可选方法不是协议必须实现的方法。 使用@required和@optional指令 (后跟方法的名称),可以将方法分别标记为必选方法和可选方法。 如果你没有使用这些关键字,默认设置为必选方法。 例:类Writer 声明一个write方法,该方法将NSFileHandle的引用做参数并且不返回任何值

    声明协议

    #import <foundation/Foundation.h> @protocol Writer <NSObject> -(void)write:(NSFileHandle *)file; @end

    接受协议

    #import <foundation/Foundation.h> #import 'Writer.h' @interface Atom:NSObject<Writer> @end 在实现文件中执行方法 -(void)write:(NSFileHandle *)file{ }

    6.动态绑定、内存管理

    (1)动态绑定

    动态绑定确定在运行时而不是在编译时调用的方法。 动态绑定也称为后期绑定。 在Objective-C中,所有方法都在运行时动态解析。执行的确切代码由方法名称(选择器)和接收对象确定。 动态绑定可实现多态性。 例如,考虑一组对象,包括Rectangle和Square。 每个对象都有自己的printArea方法实现。

    表达式[anObject printArea]执行的实际代码是在运行时确定的。 运行时系统使用方法运行的选择器来识别anObject的任何类中的适当方法。

    (2)内存管理

    Objective-C内存管理技术大致可分为两类

    “手动保留或释放”或MRR“自动参考计数”或ARC

    MRR

    在MRR中,通过跟踪自己的对象来明确管理内存。这是使用一个称为引用计数的模型实现的,Foundation类NSObject与运行时环境一起提供。 MRR和ARC之间的唯一区别是保留和释放,前者是手动处理,而后者则自动处理。

    下图表示内存管理在Objective-C中的工作方式示例。

    A类对象的内存生命周期如上图所示。 如您所见,保留计数显示在对象下方,当对象的保留计数变为0时,对象将被完全释放,并且其内存将被释放以供其他对象使用。 首先使用NSObject中提供的alloc/init方法创建A类对象。 现在,保留计数变为1。 现在,B类保留了A类的对象,A类对象的保留计数变为2。 然后,C类拷贝该对象的副本。它被创建为A类的另一个实例,具有相同的实例变量值。 这里,保留计数是1而不是原始对象的保留计数。如图中的虚线表示。 使用release方法由C类释放复制的对象,并且保留计数变为0,因此对象被销毁。 对于初始的A类对象,保留计数为2,所以必须释放两次才能销毁它。 这是通过A类和B类的释放语句完成的,它们将保留计数分别减少到1和0。 最后,对象就被销毁了。

    MRR基本规则

    拥有创建的任何对象:使用名称以“alloc”,“new”,“copy”或“mutableCopy”开头的方法创建对象使用retain获取对象的所有权:通常保证接收到的对象在接收到的方法中保持有效,并且该方法也可以安全地将对象返回给它的调用者。在两种情况下使用retain - a.在访问器方法或init方法的实现中,获取想要存储为对象属性值的对象的所有权。 b.防止对象因某些其他操作的副作用而失效。当不再需要它时,必须放弃对拥有的对象的所有权:通过向对象发送释放消息或自动释放消息来放弃对象的所有权。 因此,在Cocoa术语中,放弃对象的所有权通常被称为“释放”对象。不得放弃不拥有的对象的所有权。 #import <Foundation/Foundation.h> @interface SampleClass:NSObject - (void)sampleMethod; @end @implementation SampleClass - (void)sampleMethod { NSLog(@"Hello, World! \n"); } - (void)dealloc { NSLog(@"Object deallocated"); [super dealloc]; } @end int main() { /* 第一个Objective-C程序 */ SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass sampleMethod]; NSLog(@"Retain Count after initial allocation: %d", [sampleClass retainCount]); [sampleClass retain]; NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]); [sampleClass release]; NSLog(@"Retain Count after release: %d", [sampleClass retainCount]); [sampleClass release]; NSLog(@"SampleClass dealloc will be called before this"); // 应该将对象设置为nil sampleClass = nil; return 0; }

    执行结果

    2019-05-16 07:02:42.556 main[152785] Hello, World! 2019-05-16 07:02:42.558 main[152785] Retain Count after initial allocation: 1 2019-05-16 07:02:42.558 main[152785] Retain Count after retain: 2 2019-05-16 07:02:42.558 main[152785] Retain Count after release: 1 2019-05-16 07:02:42.558 main[152785] Object deallocated 2019-05-16 07:02:42.558 main[152785] SampleClass dealloc will be called before this

    ARC

    在自动引用计数或ARC中,系统使用与MRR相同的引用计数系统,但它在编译时为我们插入适当的内存管理方法调用。 强烈建议将ARC用于新项目。 如果使用ARC,通常不需要理解本文档中描述的底层实现,尽管在某些情况下它可能会有所帮助。 有关ARC的更多信息,请参阅ARC发行说明。 如上所述,在ARC中,不需要添加release和retain方法,因为编译器会对此进行处理。 实际上,Objective-C的基本过程仍然是相同的。 它在内部使用保留和释放操作,使开发人员更容易编码而无需担心这些操作,这将减少写入的代码量和内存泄漏的可能性。 还有另一个原则叫做垃圾收集,它在Mac OS-X中与MRR一起使用,但由于它在OS-X Mountain Lion中的弃用,它还没有与MRR一起讨论过。 此外,iOS对象从未拥有垃圾收集功能。 使用ARC,OS-X中也没有使用垃圾收集。 这是一个简单的ARC示例。 请注意,这不适用于在线编译器,因为它不支持ARC。

    #import <Foundation/Foundation.h> @interface SampleClass:NSObject - (void)sampleMethod; @end @implementation SampleClass - (void)sampleMethod { NSLog(@"Hello, World! \n"); } - (void)dealloc { NSLog(@"Object deallocated"); } @end int main() { /* my first program in Objective-C */ @autoreleasepool { SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass sampleMethod]; sampleClass = nil; } return 0; }

    执行结果

    2019-05-28 08:45:17.210 demo[8385] Hello, World! 2019-05-28 08:45:17.211 demo[8385] Object deallocated

    7.Runloop和Runtime

    (1)Runloop 程序执行是一行行执行,执行完毕程序就完了。可为什么iOS项目应用可以在我们用的时候一直在开启,而没有结束语句 main函数就是包括了一个封闭循环Runloop

    它可以接受三种方法

    系统端口方法,就是本来就写在系统之中方法调用。自定义方法,表现在我们对手机做各种手势触摸操作,其实这也算是系统方法,因为不同的手势还说调用系统写好的方法,再者就是我们手动添加到runloop的方法,这个正在研究,比如遇到多线程问题,有时候一个任务添加到线程不执行,是因为这个任务没有调用该线程的runloop。就是定时发生的事件,NSTimer。很多时候程序中需要设置一些某些特定情况写自动触发的事件就需要用到定时器,而这些事件并不是人为添加的,也不是系统执行就添加的,而是在某些情况下添加的。 注:runloop是oc消息机制的执行阶段产物。​

    (2)Runtime runtime是实现oc变成动态编译语言的基本,一个c写的库。就我理解来说,它是一个库,一个对oc里面那些特殊语法的具体实现。比如[]调用方法,.方法调用属性,等等, 这些功能的实现,而且runtime里面那些get,add,replace等方法的参数也是我们在oc中设置的属性,方法,协议等东西。然后还有方法的实现和声明能连接到一起的原因及根据这个原理来做一些oc做不到的事等等,runtime最核心的东西,苹果的消息机制。 我们在调用方法,属性甚至block的时候,数据信息是如何传递的,如何定位的。这些问题都能在objc_sendmsg()中找到答案,我们知道,苹果为了优化性能,runtime是一个c写的库,但是在一些调用非常频繁的函数中,直接使用汇编进行编写(丧心病狂max)。还有缓存策略,异常处理策略等,对我以后做项目真的有非常大的帮助。

    8.基础框架

    Cocoa不是一种编程语言(它可以运行多种编程语言),它也不是一个开发工具(通过命令行我们仍然可以开发Cocoa程序),它是创建Mac OS X和IOS程序的原生面向对象API,为这两者应用提供了编程环境。 我们通常称为“Cocoa框架”,事实上Cocoa本身是一个框架的集合,它包含了众多子框架,其中最重要的要数“Foundation”和“UIKit”。前者是框架的基础,和界面无关,其中包含了大量常用的API;后者是基础的UI类库,以后我们在IOS开发中会经常用到。

    Foundation:

    创建和管理集,如数组和字典访问储存在应用程序中的图像和其他资源创建和管理字符串发布和观察通知创建日期和时间对象自动发现 IP 网络上的设备操控 URL 流异步执行代码

    Foundation框架的NSObject对象

    UIKit:

    构建和管理用户界面处理基于触摸和运动的事件显示文本和网页内容优化应用程序以实现多任务创建自定用户界面元素

    UIKit主要用于界面构架

    最新回复(0)