iOS—EffectiveObjective—C2.0(1)-创新互联
- 了解OC起源
- 消息结构和函数调用之间的区别
- 要点
- 虚方法表
- 在类头文件中尽量少引入其他头文件
- 要点
- 多用字面量语法,少用与之等价的方法
- 字面量数值
- 字面量数组
- 字面量字典
- 可变数组与字典:
- 局限性:
面向对象语言OC,在语法上使用的是“消息结构”,而非“函数调用”
消息结构和函数调用之间的区别//messaging(OC)
object* obj = [object new];
[obj performWith:parameter1 and:parameter2];
//function calling (C++)
object* obj = new object;
obj ->perform(parameter1, parameter2);
关键区别:使用消息结构的语言,其运行时所应执行的代码由运行环境来决定;而使用函数调用的语言则由编译器来决定。
如果范例代码中调用的函数是多态的,那么在运行时就要按照“虚方法表”来查出到底应该执行哪个函数实现。而采用消息结构的语言,不论是否多态,总是在运行时才会去查找所要执行的方法
实际上,编译器不关心接受消息的对象是何种勒烯女。接收消息的对象问题也要在运行时候处理,其过程叫做“动态绑定”OC的重要工作都由“运行期组件”而非编译器来完成。
使用OC的面向对象特性所需的全部数据结构及函数都在运行期组件李敏啊。举例来说,运行期组件中含有全部的管理内存的方法。运行期组件本质上就是一种与开发者所编代码相链接的“动态库”,其代码能把开发者编写的所有程序粘合起来。
这样的话,只需要更新运行期组件,即可提升应用程序性能。而那种许多工作都需要在“编译期”完成的语言,若是想要获得类似的性能提升,则需要重新编译应用程序代码。
OC是C的“超集”所以c语言中的所有功能在编写OC代码的时候依然适用。因此必须同时掌握C与OC这两门语言的核心概念,才能写出高效的OC代码来。
其中尤为重要的是要理解C语言的内存模型,这有利于理解OC的内存模型及“引用计数”机制的工作原理。
理解内存模型:OC中的指针是用来指示对象的,想要声明一个变量,令其指代某个对象,可用以下语法NSString* someString = @"THE String";
这种语法基本上是照搬c语言的,他声明了一个名为someString的变量,类型为NSString*也就是说,此变量为指向NSString的指针,所有OC语言的对象都必须这样声明,因为所占的内存总是分配在“堆空间”中,而绝不会分配在“栈上”,不能在栈上分配OC对象
someString变量指向分配在堆里的某块内存,其中含有一个NSString对象。也就是说,如果再创建一个变量,令其指向同一地址,那么并不拷贝该对象,只是这两个变量会同时指向此对象
这说明当前“栈帧”里面分配了两块内存,每块内存的大小都能容下一枚指针(32位4字节,64位8字节)。两块内存里面的值是一样的,就是NSString实例的内存地址
分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理。
OC将堆内存管理抽象出来了。不需要用malloc和free来分配或释放对象所占内存。OC运行期环境把这部分工作抽象为一套内存管理架构,名叫“引用计数”
在OC代码中有时会遇到定义不含*的变量,他们可能会使用栈空间,这些变量保存的不是OC对象 CGRect就是例子
CGRect是c结构体
struct CGRect {CGpoint origin;
CGSize siza;
};
typedef struct CGRect CGRect;
整个系统框架都在使用这种结构体,如果改用OC对象来做的话,性能会受影响
,与创建结构体相比,创建对象还需要额外开销,例如分配释放堆内存等,如果只需要保存 int float double char等“非对象类型”那么通常使用CG热潮天、这种结构体就行
- OC为C语言添加了面向对象的特性,是其超集。OC使用动态绑定的消息结构,在运行时才会检查对象类型。接收一条消息之后,在接受一条消息后,应该执行何种代码,由运行期环境而非编译器来决定。
- 理解c语言的核心概念有助于写好OC程序,尤其要掌握
内存模型
与指针
virtual method table 是编程语言为实现“动态派发”或“运行时方法绑定”而采用的一种机制。
在类头文件中尽量少引入其他头文件与C和C++一样使用头文件与实现文件来区隔代码,用OC语言编写“类”的标准方式为:以类名做文件名,分别创建两个文件,头文件后缀用.h,实现文件后缀用.m,创建好一个类之后,其代码看上去如下所示
//
// EOCPerson.h
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#importNS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString* firstName;
@property (nonatomic, copy) NSString* lastName;
@end
NS_ASSUME_NONNULL_END
// EOCPerson.m
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#import "EOCPerson.h"
@implementation EOCPerson
@end
OC语言编写任何类基本都需要引入Foundation框架,如果不引入这个文件的话,那么就要引入与其超类所属框架相对应的“基本头文件”,在创建iOS应用程序时,通常会继承UIViewController类,而这些子类的的头文件需要引入UIKit.h
EOCPerson类其头文件中引入了整个Foundation框架,那如果此类继承自Foundation框架中的某个类,那么EOCPerson类的使用者可能会用到其基类的中的许多内容。继承自UIViewController的那些类也是如此,其使用者可能会用到UIKit中的大部分内容。
你创建了一个新类EOCEmployer,然后你觉得每个EOCPerson实例都需要有一个EOCEmployer于是添加了一个属性@property (nonatomic, strong) EOCEmployer *employer;
但你需要引入#import "EOCEmployer.h"
,这种办法可行,但是不够优雅,在编译一个使用了EOCPerson类的文件时,不需要知道EOCEmployer的具体细节,只需要知道有一个类名叫EOCEmployer就好,有一个办法可以将这一情况告诉编译器
@class EOCEmployer;
“向前声明”,现在EOCPerson的头文件就是这样
// EOCPerson.h
// EOC1.1
//
// Created by zzy on 2022/12/22.
//
#import@class EOCEmployer;
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString* firstName;
@property (nonatomic, copy) NSString* lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end
EOCPerson类的实现文件则需要引入EOCEmployer类的头文件,因为若要使用后者,则需要知道所有接口细节,实现文件就需要引入#import "EOCEmployer.h"
,将引入头文件的时机尽量延后,只有在确有需要时才引入,以减少类的使用者所引入的头文件数量,假设本例把EOCEmployer.h引入到EOCPerson.h,那么只需要引入EOCPerson.h就可以一并引入EOCEmployer的所有内容了,但这样需要引入许多根本用不到的内容,增加了不必要的编译时间。
- 向前声明也解决了两个类互相引用的问题。如果在各自的头文件引用了对方的头文件,会导致循环引用,当解析其中的一个头文件时编译器会发现它引入了另一个头文件,而那个头文件又回过头来引用第一个头文件,虽然使用import而非include虽然不会导致死循环,但这意味着两个类里面有一个无法被正确编译。
有时候必须要在头文件中引入其他头文件。如果你写的类继承自某个超类,则必须引入定义那个超类的头文件。
同理,若要声明你写的类遵从某个协议,那么该协议必须拥有完整定义,且不能使用向前声明,向前声明只能告诉编译器有这个协议,而此时编译器是需要知道该协议中定义的方法。
若要因为实现属性,实例变量或者要遵循协议而必须引入头文件,则应尽量将其移至“class—continuation”分类中,这样做不仅可以缩短编译时间,而且还能降低彼此的依赖程度,若是依赖关系太过复杂,则会给维护带来麻烦,而且,如果只是想把代码的某个部分开放为公共API的话,太复杂的依赖关系也会出问题
- 除非有必要,否则不要引入头文件,一般来说,应该在某个类的头文件中使用向前声明来提及别的类,并在实现文件中英茹那些类的头文件,这样做可以尽量降低类之间的耦合
- 有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把该类遵循某协议的这条声明移动至“class-continuation分类中”如果不行的话,就把协议单独放在一个头文件中,然后将其引入。
在使用oc时要经常用到Foundation框架,经常用到NSString,NSNumber,NSArray,NSDictionary。从类名上即可看出各自所表示的数据结构。
有一种方式能非常简单的方式能创建NSString对象:“字符串字面量”,NSString *someString = @"EOC2.0";
如果不用这种语法的话就要用常见的alloc init方法来分配并初始化NSString对象了,这种方法可以缩减源代码长度,使其更为易读。
能够以NSNumber实例表示的所有 数据类型都可以使用该语法。
NSString *someString = @"EOC2.0";
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
//字面量语法也适用于以下表达式;
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
字面量数组数组是常用的数据结构,非字面量语法创建实例:
NSArray *season = [NSString arrayWithOjbects:@"spring",@"summer",@"autumn",@"winter",nil];
使用字面量语法:
NSArray *season = @[@"spring",@"summer",@"autumn",@"winter"];
这种做法不仅简单,而且利于操作数组。数组常见操作就是取某个下标所对应的对象,如果不用字面量操作:
NSString *sum = [season objectAtIndex:1];
使用字面量:
NSString *sum = season[1];
要注意,若数组元素对象中有nil,则会抛出异常,字面量语法实际上只是一种语法糖,其效果等于是先创建了一个数组,然后把方括号内的所有对象都添加进数组,如果发现nil则会停止添加(arrayWithObjects:方法会依次处理各个参数,直到发现nil为止)
使用字面量语法会更为安全,出现异常会令程序终止执行,这比创建好数组之后才发现元素个数少了要好。
Dictionary ,映射型数据结构,键值对
NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys:
@"Matt", @"firstName",
@"Galloway", @"lastName",
[NSNumber numberWithInt:28], @"age",
nil];
顺序是 《对象》,《键》 与
通常理解为 把“键”映射到“对象”,相反
字面量:
NSDictionary *personData = @{@"firstName" : @"Matt",
@"lastName" : @"Galloway",
@"age" : @28};
28区别原因:字典中的对象和键必须都是Objective-C对象,所以不能把28直接放进去,而要封装在NSNumber实例中才行
由键访问其值 : 不用字面量: NSString *lastNamge = [personData objectForKey:@“lastName”];
使用字面量:NSString *lastName = personData[@“lastName”];
可变数组与字典:修改可变数组与字典内容的标准做法是:
[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableDictionary setObject:@"Galloway"forKey:@"lastName"];
换成去下标:
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
局限性:除了字符串以外,所创建出来的对象必须属于Foundation框架。
使用字面量语法创建出来的字符串、数组、字典对象都是不可变的(immutable)
想要可变版本的对象,需复制一份:NSMutableArray *mutable = [@[@1, @2, @3]mutableCopy];
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
本文标题:iOS—EffectiveObjective—C2.0(1)-创新互联
URL地址:http://azwzsj.com/article/docjph.html