Objective-C 2.0 基础要点归纳
本文的阅读基本条件:
- 具备C/C++基础知识,了解面向对象特征
- 阅读过《Objective-C 2.0 程序设计(第二版)》、《Objective-C 程序设计 第6版》或相关基础OC书籍
知识要点汇总模式: 提出问题。给出详细解释(Q&A)
PS:会以扩展方式延伸介绍一些知识点
问题列表
Q1. Objective-C特点以及和C++的差别?
Q2. 属性的特点。和实例变量的差别,使用注意事项?
Q3. 类的继承。协议和分类的概念和使用,以及须要注意的问题?
Q1. Objective-C特点以及和C++的差别?
Objective-C特点
Objective-C(OC)是C的超集,基于C语言加入了面向对象特性和消息转发机制的动态语言(继承了smalltalk语言面向对象的经典思想)。
除编译器外还须要用执行时(runtime)系统来动态创建类和对象进行消息发送和转发,而runtime系统是一个用C语言编写动态链接库(libobjc.A.dylib)。 该库提供了OC语言所需的各种动态特性的支持。
OC採用消息传递(Messaging)即向对象传递消息的方式而非方法调用。如: [receiver message],编译器并不立即执行对象的message方法,而是向receiver对象发送一条message消息,编译器将其转化为 obj_msgSend (runtime和对象模型详细实现參见Foundation框架要点归纳)及其相关函数调用,在程序执行时会依据obj_msgSend 查找详细方法实现。
简单的说,即OC程序在编译时并未详细确定方法实现,而是在执行时借助runtime系统实现方法的动态绑定,通过obj_msgSend实现查找方法的类别。进而调用方法实现,通过runtime系统实现面向对象的多态特性(不同的类有同样的方法名)。
C++ 和 OC的差别
C++ 也是一种对C实现面向对象特性的扩展,能够从面向对象的三个特性(封装、继承/派生、多态)角度简单分析C++和OC的不同
封装: OC和C++都有自己风格的类结构语法定义方式。OC成员变量默觉得protected。属性是OC的一个特性,用于封装对象中的数据。C++默觉得private。 OC能够通过分类对原始类进行扩充,在不破坏类封装特性的基础上对类进行方法加入,但不能添加成员变量(属性能够)。
继承: C++能够多继承。即同一时候继承多个父类。 OC仅仅能单继承,但通过协议有效添加了继承的灵活性和多样性,弥补了单继承的缺点。
多态: 即同样类具有同名函数(方法)。但能够通过类别来区分实现相应的函数(方法)。C++和OC不同的是其对象採用函数调用方式,且在编译阶段实现类和函数的绑定(虚函数的动态绑定机制除外),这点和OC具有明显的差别,OC是在执行时通过runtime系统来查找详细类别方法并实现。
并且,C++能够实现函数重载功能,OC没有该功能。
Q2. 属性的特点,和实例变量的差别,使用要点?
属性的特点
属性(property)是OC一个特性,用于封装对象中的数据。OC通过定义实例变量来存储对象所需的数据。并通过存取方法(access method。setter,getter)来訪问。
上述概念的成型且经由“属性”这一特性而成为OC 2.0 一部分。
使用“属性”代替定义传统的实例变量和存取方法让编译器自己主动合成存取方法有利于提高程序性能(能够通过@dynamic关键字告诉编译器不要实现属性所用的实例变量和存取方法)。而引入了点语法(dot syntax)更提高程序可读性(”.”语法实际上编译成消息传递模型[receiver message])。
属性关键字(特质)
property有一些具有特殊用途的关键字(特质),一般分为三类:原子性。存取器控制,内存管理
(1)原子性
atomic(默认):仅仅同意一个线程訪问实例变量。线程安全效率低下
nonatomic: 能够被多线程訪问,效率高
(2) 读写权限
readwrite(默认):当属性通过@synthesize实现时,编译器自己主动生成setter和getter方法
readonly :仅仅有属性由@synthesize实现时,编译器才会合成获取方法,仅仅有 getter没有setter
(3)内存管理
显示内存管理策略
assign(默认):用于值类型,如 int,float,NSInteger 等表示单纯的复制
retain: 在setter方法中。须要对传入对象进行引用计数+1的操作,即对对象具有全部权,该对象不会被释放
strong:和retain意思同样。并产生同样代码,但语意上更能体现对象拥有
weak:setter方法中对传入对象不进行引用计数+1的操作,即对传入的对象没有全部权。当对象引用计数为0时,对象被释放,声明实例变量指向nil
unsafe_unretained: 和assign同样,可是它适用于“对象类型”,非拥有关系,当目标对象被清除时,该属性之不被清空(nil, 和weak有差别), 訪问会造成崩溃。
copy: 和strong相似。但差别在于对对象副本拥有全部权而非对象本身。经常使用于NS String * 类型。用于保护其封装性。
property使用要点
- 在implementation中假设不用@synthesize, 则使用属性时能够通过 _name(编译器隐藏了 @synthesize name = _name;) 或者self.name , [self name]訪问,点运算符和调用默认getter方法效果一样。
- 但假设自己定义了getter或者setter方法,则在方法实现中不能够使用 self ,须要用_name对实例变量进行訪问。否则 会进入死循环, 以下的Demo代码中有体现
- 假设使用@synthesize,则能够直接使用变量 name
浅复制(浅拷贝)和深复制(深拷贝)
概念解释:
浅复制: 对于对象中的每一层(对象成员中包括的对象)复制都是指针复制(引用计数角度,每层对象引用计数+1)
深复制:至少有一个对象复制是对象内容复制从引用计数角度出发,除了原对象,其它指针复制的对象引用计数都+1)
方法关联:
retain:始终採取浅复制。引用计数+1
copy: 对于不可变对象,copy 採用的是浅复制。引用计数+1(编译器进行的优化)
对于可变对象copy採用的是深复制,引用计数器不变
mutableCopy: 可变和不可变对象都採用深复制
属性修饰词关联:
retain , strong 都是对象引用计数+1
copy 是拷贝对象副本,引用计数+1
对于常量类型 assign
NSString类 copy
id对象 strong
关联对象 weak
非系统内存管理 unsafed_retained
// ------AClass.h------
#import <Foundation/Foundation.h>
@interface AClass : NSObject
// property
@property (nonatomic, strong) NSString *aName;
// print
- (void)print;
// self-defined getter & setter
// 主要和 自己定义getter方法作比較。通过获取的限制条件
- (NSString *)aName;
@end
// ------AClass.m------
@implementation AClass
//@synthesize aName = _aName; // 系统隐藏
//@synthesize aName;
// print
- (void)print
{
// 调用实例变量
NSLog(@"内部Print方法直接调用实例变量%@", _aName);
// 调用自己定义getter函数
NSLog(@"内部Print方法通过存取方法获取属性%@", self.aName);
NSLog(@"内部Print方法通过存取方法获取属性%@", [self aName]);
}
// getter
- (NSString *)aName
{
if ([_aName isEqualToString:@"King"]) { // self.aName 会进入死循环
return _aName;
}
else {
return @"Not King~";
}
}
@end
// ------main.m------
int main(int argc, char const *argv[])
{
@autoreleasepool {
AClass *a = [[AClass alloc] init];
a.aName = @"King";
NSLog(@"通过点运算符实现属性訪问: %@", a.aName);
// 调用存取方法的setter
[a setAName:@"King2"];
NSLog(@"通过存取方法实现属性訪问: %@", [a aName]);
NSLog(@"----------------------------------------");
[a print];
return 0;
}
}
OutPut:
通过点运算符实现属性訪问: King
通过存取方法实现属性訪问: Not King~
----------------------------------------
内部Print方法直接调用实例变量King2
内部Print方法通过存取方法获取属性Not King~
内部Print方法通过存取方法获取属性Not King~`
Q3. 类的继承,协议和分类的概念和使用。以及须要注意的问题?
知识点汇总
(Maybe you need it or learn something important)
[继承] OC是面向对象语言,继承方式和C++有明显差别即单继承,而没有C++的多继承。但多协议弥补了不能多继承的缺陷。能够通过子类继承的方式
a.添加新的方法/或实例变量,
b.类的特别接口封装。
c. 覆写 一个或者多个方法改变类的默认行为[多态,动态类型和动态绑定]
多态: 不同的类对象定义同样名称
动态类型id类型: 直到执行时才确定所属对象的类,通用对象类型,定义为:
typedef struct objc_object *id;
(id 变量不能使用点运算符)
动态绑定:执行时才确定实际要调用的对象方法[分类 - Category]
提供一种简单方式将类定义模块化到相关方法的组或分类中。一般。假设主类为B,则分类头文件和实现文件名分别为:B+subClass.h, B+subClass.m
从调用角度,分类就是对原始类的一种扩展
从程序角度。分类可读性更强
分类文件格式
#import “SuperClassName.h”
@interface Fraction (MathOp)
@end
类扩展:
假设创建一种未命名的分类。则称为类的扩展,在有命名的分类中时不同意的。和有命名分类不同,未命名分类主要在主实现区实现,而非分离实现区域。未命名分类扩展的方法和属性或者实例变量仅仅能由该类本身私有。
Category 使用场景:
a. 已经定义的类须要加入新的方法功能
b. 一个类中包括很多种不同类型方法,须要不同团队实现,有利于任务分配
注意问题:
a. Category能够訪问原始类方法。但不能加入变量,加入变量能够通过创建子类(继承)方式来实现
b. Category能够重载原始方法,会导致不能訪问原来的方法,创建子类实现 重载 覆写
c. 和普通接口有所差别,分类实现文件里能够不必实现全部声明的方法
- [协议]
协议 即多个类共享一个方法列表,协议中列出的方法没有相应的实现,通过文档说明,使用协议中的方法能够通过文档说明进行实现。
一系列不属于不论什么类的方法列表,当中声明的方法能够被不论什么类实现。
这样的模式称为代理模式。
在不同场景中实现不同模式。Apple採用了大量的代理模式来实现 MVC中 View和 Controller 的解耦。
最经常使用的是托付代理模式。 Cocoa框架中大量採用这样的模式实现 数据和UI的分离: UIView产生的全部事件 都是通过托付的方式 交给 Controller 完毕
框架中后缀为Delegate都是 Protocol
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
(能够放在单独的.h头文件里定义。也能够放在相关类的h文件里)
使用:
@interface AddressBook : NSObject< NSCopying, NSCoding >
PS:
不是必需在接口部分声明协议的方法。但要在实现部分定义这些方法
2. 能够通过 例如以下代码来检查一个对象是否遵循某项协议
id currentObject;
if ([currentObject conformsToProtocol:@protocol(Drawing)] == YES) {
}
3. 也能够使用 respondsToSelector:@selector() 来检查是否实现了可选的方法
代理(delegation)
-(BOOL)respondsToSelector:selector 被广泛用于实现托付方法定义
4. id currentObject; 借助编译器来检查变量一致性
5. 和类名一样,协议名必须唯一
6. 协议是能够继承的,具有继承的属性。假设协议B继承了协议A,则实现协议B 须要实现A和B的全部方法- [代理] delegation
协议是一种两个类之间的接口定义,定义了协议的类能够看作是将协议定义的方法代理给实现他们的类。(定义了协议的类称为 代理)
- [代理] delegation
測试程序
//******測试样例: (涵盖之前一些内容,算是整合一下思路)
//******MTProtocol.h 文件,为定义的协议
@protocol Printing
// 默认
@required
- (void)printProtocol;
@optional
- (void)printProtocolOptional;
@end
//******A.h 文件,包括A类定义。採用Printing协议
#import <Foundation/Foundation.h>
#import "MTProtocol.h"
// 接口处定义实例变量, 採用协议
@interface A : NSObject <Printing>
{
int x;
}
@property (nonatomic, assign) int y;
- (void)initXY;
- (void)printXY;
@end
//******A.m 文件,包括A类实现,注意实例变量和属性初始化的问题(这里没有重载init方法,所以不须要模版 self = [super init] 。。。)
#import "A.h"
@implementation A
- (void)initXY
{
x = 1;
self.y = 2;
}
- (void)printXY
{
NSLog(@"This is Class A : %i, %i", x, self.y);
}
- (void)printProtocol
{
NSLog(@"I am printProtocol Method form Printing!");
}
- (void)printProtocolOptional
{
NSLog(@"I am printProtocolOptional Method form Printing!");
}
@end
//******A+Op.h A类的分类接口,用于扩展(感觉就是扩展。一方面通过子类。一方面在原类中进行分类扩展,或者 合成类 这个比較奇葩)
#import "A.h"
@interface A (Op)
- (void)printCategory;
@end
//******A+Op.m A类的分类实现
#import "A+Op.h"
@implementation A (Op)
- (void)printCategory
{
NSLog(@"Op Category for Class A!");
}
@end
//******B.h B类接口,A 类子类。在B类中我们尝试 未命名分类,即私有方法扩展
#import "A.h"
@interface B : A
- (void)initXY;
- (void)printXY;
@end
//******B.m B类实现,注意未命名分类
#import "B.h"
@interface B ()
- (void)printNanNameCategory;
@end
@implementation B
- (void)initXY
{
x = 10; // A类中接口处定义了实例变量,能够被继承
super.y = 20; // 属性或者实现部分声明的变量为私有实例变量。通过合成取值方法获取
}
- (void)printXY
{
NSLog(@"This is Class B : %i, %i", x, self.y);
}
- (void)printNanNameCategory
{
NSLog(@"I am Nan Name Category for B!");
}
@end
//******main.m 实现,注意未命名分类
#import "A+Op.h"
#import "B.h"
// ---------- main ----------
int main(int argc, const char * argv[]) {
@autoreleasepool {
A *a = [[A alloc] init];
B *b = [[B alloc] init];
[a initXY];
[b initXY];
// 定义动态类型 id
id tmp = a;
[tmp printXY];
[tmp printCategory];
[tmp printProtocol];
[tmp printProtocolOptional];
tmp = b;
[tmp printXY];
[tmp printProtocolOptional];
// 測试 tmp 是否是A类别
//[tmp isKindOfClass:[A class]] ?
NSLog(@"Yes") : NSLog(@"No");
// 測试A 类是否包括printXY方法
//[A instancesRespondToSelector:@selector(printXY)] ? NSLog(@"Yes") : NSLog(@"No");
// 測试对象a 是否包括init方法
//[a respondsToSelector:@selector(init)] ? NSLog(@"Yes") : NSLog(@"No");
}
}
结果分析
This is Class A : 1, 2
Op Category for Class A!
I am printProtocol Method form Printing!
I am printProtocolOptional Method form Printing!
This is Class B : 10, 20
I am printProtocolOptional Method form Printing!
參考资源
- 《Effective Objective-C2.0》
- 《Objective-C 2.0 程序设计(第二版)》/《Objective-C 程序设计 第6版》
- Objective-C消息传递机制
- 深入理解Objective-C的Runtime机制
- Objective-C的对象模型与执行时
- Objective-C 消息、Category和Protocol
Objective-C 2.0 基础要点归纳的更多相关文章
- Swif语法基础 要点归纳(一)
常量和变量 用let声明常量 let m = 20 用var声明变量 var n = 0 类型推导机制 声明常量或变量时.能够不指定常量/变量类型,编译器会依据 ...
- HTML基础要点归纳
一.开发环境 常用的HTML编辑器有Sublime Text.Hbuild.Dreamweare.以及vs code.pycharm等都可以.我目前在用的就是Sublime text3和Hbuild两 ...
- Android程序开发0基础教程(一)
程序猿学英语就上视觉英语网 Android程序开发0基础教程(一) 平台简单介绍 令人激动的Google手机操作系统平台-Android在2007年11月13日正式公布了,这是一个开放源码的操 ...
- 0基础搭建Hadoop大数据处理-编程
Hadoop的编程可以是在Linux环境或Winows环境中,在此以Windows环境为示例,以Eclipse工具为主(也可以用IDEA).网上也有很多开发的文章,在此也参考他们的内容只作简单的介绍和 ...
- Python 0基础开发游戏:打地鼠(详细教程)VS code版本
如果你没有任何编程经验,而且想尝试一下学习编程开发,这个系列教程一定适合你,它将带你学习最基本的Python语法,并让你掌握小游戏的开发技巧.你所需要的,就是付出一些时间和耐心来尝试这些代码和操作. ...
- 手把手0基础项目实战(一)——教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)...
原文:手把手0基础项目实战(一)--教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)... 本文你将学到什么? 本文将以原理+实战的方式,首先对& ...
- CORS基础要点:关于dataType、contentType、withCredentials
事实上,面试时我喜欢问跨域,因为多数开发者都知道它并且常用,而我希望能从面试者的回答中知道他在这个问题的深入程度,进一步看看面试者研究问题的思维方式及钻研精神,然而确实难到了很多人,当然这也不是面试通 ...
- <-0基础学python.第一课->
初衷:我电脑里面的歌曲很久没换了,我想听一下新的歌曲,把他们下载下来听,比如某个榜单的,但是一首一首的点击下载另存为真的很恶心 所以我想有没有办法通过程序的方式来实现,结果还真的有,而且网上已经有有人 ...
- Android 工程在4.0基础上混淆
Android现在对安全方面要求比较高了,我今天要做的对apk进行混淆,用所有的第三方工具都不能反编译,作者的知识产权得到保障了,是不是碉堡了. 一,首先说明我这是在4.0基础上进行的. 先看看pro ...
随机推荐
- Android(java)学习笔记203:JNI之NDK开发步骤
1. NDK开发步骤(回忆一下HelloWorld案例): (1)创建工程 (2)定义native方法 (3)创建jni文件夹 (4)创建c源文件放到jni文件夹 (5)拷贝jni.h头文件到jni目 ...
- Java基础(十二)--clone()方法
Clone在Java中就是用来复制对象,通过分配一个和源对象相同大小的内存空间,然后创建一个新的对象,那么他和=的区别在哪? 通过=实现对象拷贝: @Data @NoArgsConstructor @ ...
- IO 双引号 输出 输入
#! /usr/bin/perl use strict;use warnings; print "\n---------<STDIN>_store_into_an_array_a ...
- javascript——js string 转 int 注意的问题——parseInt(转)
<script> var str='1250' ; alert( Number(str) ); //得到1250 alert(parseInt(str)); //得到12 ...
- 第1节 MapReduce入门:11、mapreduce程序的入门-2
1.5.WordCount示例编写 1.JobMain.java类 package cn.itcast.wordcount; import org.apache.hadoop.conf.Configu ...
- 2D热力图实例
<div style="height: 100px; width: 200px" id="heatmap"></div> <scr ...
- 笔试算法题(26):顺时针打印矩阵 & 求数组中数对差的最大值
出题: 输入一个数字矩阵,要求从外向里顺时针打印每一个数字: 分析: 从外向里打印矩阵有多重方法实现,但最重要的是构建合适的状态机,这样才能控制多重不同的操作: 注意有四种打印模式(左右,上下,右左, ...
- [Python3网络爬虫开发实战] 1.9.5-Scrapyrt的安装
Scrapyrt为Scrapy提供了一个调度的HTTP接口,有了它,我们就不需要再执行Scrapy命令而是通过请求一个HTTP接口来调度Scrapy任务了.Scrapyrt比Scrapyd更轻量,如果 ...
- 基于Vue的简单日历组件
日历组件 由于移动端项目中需要用到日历组件,网上找了下,没看到几个合适的,就尝试着自己写一个.然后发现也不是很复杂,目前只做了最基本的功能,大家也可以拿去做做二次开发. 如何写一个日历组件 基础效果如 ...
- c++基础_矩阵乘法
#include <iostream> using namespace std; int main(){ int a,b; cin>>a>>b; long c[a] ...