iOS 应用开发(三)OC 面向对象之三大特性
面向对象的三大特性:封装、继承和多态。
unrecognized selector sent to instance 0x600002dd8040'
没有找到实例方法 或 对象被回收了。
(一)封装
封装就是把数据及其操作的实现细节隐藏起来,对外公开接口(即“合理隐藏,合理暴露”)。这样可以保证数据安全性,外部不能随便访问类的成员变量,只能通过(setter,getter)方法访问。
#import <Foundation/Foundation.h>
// 封装 Score
@interface Score : NSObject {
// @public
// 成员变量名的命名以下划线开头
int _CScore;
int _OCScore;
int _totalScore;
double _averageScore;
}
// “-” 表示该方法为对象方法
- (void)setCScore:(int)cScore;
// getter 方法名与成员变量名一致,且 getter 方法名不需要带下划线
- (int)CScore;
- (void)setOCScore:(int)ocScore;
- (int)OCScore;
- (int)totalScore;
- (double)averageScore;
// 类方法都是以 + 开头(属于类本身,而且只能被类调用)
+ (void)testClass;
@end
@implementation Score
- (void)setCScore:(int)cScore {
if(cScore < 0){
cScore = 0;
}
_CScore =cScore;
_totalScore = _CScore + _OCScore;
_averageScore = _totalScore / 2;
}
- (int)CScore {
return _CScore;
}
- (void)setOCScore:(int)ocScore {
if(ocScore < 0){
ocScore = 0;
}
_OCScore =ocScore;
_totalScore = _OCScore + _CScore;
_averageScore = _totalScore / 2;
}
- (int)OCScore {
return _OCScore;
}
- (int)totalScore {
return _totalScore;
}
- (double)averageScore {
return _averageScore;
}
+ (void)testClass {
NSLog(@"类方法testClass被调用");
}
@end
int main() {
Score *score = [Score new];
[score setCScore: 90];
[score setOCScore: 95];
NSLog(@"CScore=%d分",[score CScore]);
NSLog(@"OCScore=%d分",[score OCScore]);
NSLog(@"totalScore=%d分",[score totalScore]);
NSLog(@"averageScore=%.1f分",[score averageScore]);
// OC 弱语法:在运行过程中才会检测对象是否有实现方法(可以只有实现,没有声明,但不推荐)。
// [score test];
// 类方法 无需创建对象,直接调用即可
[Score testClass];
return 0;
}
类方法 说明:可以与对象(实例)方法同名,但不能访问成员变量,且类方法只能由类本身调用。
self 关键字 说明:是一个指针,指向了方法的调用者。可以是对象,也可以是类本身;既可用在对象方法中,也可用在类方法中。
// ------- Object -----------//
- (void)printAge {
NSLog(@"test...age=%d,%p",self->_age,self);
}
- (void)test {
[self printAge];
}
// ------- Class -----------//
+ (int)sumofNum1: (int)num1 andSum2: (int)num2 {
return num1 + num2;
}
+ (double)averageOfNum1: (int)num1 andSum2: (int)num2 {
return [self sumofNum1:num1 andSum2:num2] / 2;
}
// test...
double average = [Calculator averageOfNum1:10 andSum2:20];
NSLog(@"average=%.2f",average);
(二)继承
继承的作用是抽取出了重复的代码,建立了类和类之间的联系,
如上面的 @interface Score : NSObject // Score继承了 NSObject,获得 NSObject类的方法;缺点是耦合性太强。
注意: OC 中不允许子类和父类相同成员变量名,但可以有同名方法(子类调用时,先会先找自己的,没有再找父类的),基本上 NSObject 是所有类的基类。
#import <Foundation/Foundation.h>
// 父类:Animal
@interface Animal : NSObject {
int _age;
double _weight;
}
- (void)setAge: (int)age;
- (int)age;
- (void)setWeight: (double)weight;
- (double)weight;
- (void)eat:(char *)food;
@end
@implementation Animal
// #pragma 是Xcode 特有的注释标记
#pragma mark - Animal getter & setter
- (void)setAge: (int)age {
_age = age;
}
- (int)age {
return _age;
}
- (void)setWeight: (double)weight {
_weight = weight;
}
- (double)weight {
return _weight;
}
- (void)eat:(char *)food {
NSLog(@"eat %s!",food);
}
@end
// 子类 Dog 继承 Animal (Dog is Animal)
@interface Dog : Animal
@end
@implementation Dog
- (void)eat:(char *)food {
NSLog(@"Dog eat %s!",food);
}
@end
// 子类 Cat 继承 Animal (Cat is Animal)
@interface Cat : Animal
@end
@implementation Cat
- (void)eat:(char *)food {
// NSLog(@"Cat eat %s!",food);
// 调用父类中的 eat 方法
[super eat: food];
}
@end
int main() {
Dog *dog = [Dog new];
[dog setAge: 5];
[dog setWeight: 30.0];
NSLog(@"Dog age=%d,weight=%.2f",[dog age],[dog weight]);
Cat *cat = [Cat new];
[cat setAge: 4];
[cat setWeight: 20.0];
NSLog(@"Cat age=%d,weight=%.2f",[cat age],[cat weight]);
[dog eat: "meat"];
[cat eat: "fish"];
return 0;
}
super 关键字 说明:[super eat: food]// 调用父类中的 eat 方法;如果处在 -对象方法中,那么就会调用父类的对象(实例)方法;如果处于 +类方法中,那么就会调用父类的类方法。一般用于子类在复写父类方法时,想要保留父类的一些(基本)行为。
(三)多态
多态:由继承演变而来,父类指针指向子类对象,即同一类型的对象调用同一方法,表现出不同行为。
#import <Foundation/Foundation.h>
@interface Animal : NSObject
- (void)eat;
@end
@implementation Animal
- (void)eat {
NSLog(@"eat...");
}
@end
// Dog 继承 Animal
@interface Dog : Animal
@end
@implementation Dog
- (void)eat {
NSLog(@"Dog eat meat");
}
@end
// Cat 继承 Animal
@interface Cat : Animal
@end
@implementation Cat
- (void)eat {
NSLog(@"Cat eat fish");
}
@end
int main() {
// 父类指针指向子类对象
Animal *p1 = [Dog new];
Animal *p2 = [Cat new];
// 同一类型的对象调用同一方法,表现出不同行为
[p1 eat];
[p2 eat];
return 0;
}
(四)NSString 字符串
OC 的字符串输出使用 @ 占位
#import <Foundation/Foundation.h>
int main() {
// 1. 创建字符串对象 "CloudKK",并让指针 str 指向它的首地址
NSString *name =@"CloudKK";
// OC 的字符串输出使用 @ 占位
NSLog(@"%@",name);
int age = 20;
int no = 10;
// 2. stringWithFormat:创建字符串对象(带参方法)
NSString *str = [NSString stringWithFormat:@"age is %d,and no is %d,name is %@",age,no,name];
// localizedStringWithFormat 此方法会根据系统Local进行显示
NSLog(@"%@",str);
NSLog(@"str的字符长度=%ld",[str length]);
// char *name = "CloudKK";
// NSLog(@"%s",name);
return 0;
}
(五)block 代码块
block 也是一种数据类型,但它可用来保存一段代码,用 ^{...} 表示。跟指针函数类似,可以有形参、返回值。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 示例1: 无参数
void (^myblock)(void) = ^{
NSLog(@"myblock-----");
};
myblock();
// 返回类型 (^blockName)(参数类型1,...) = ^(参数类型1:变量名,...) {
// ...
// };
int (^sumblock)(int, int) = ^(int a, int b){
return a + b;
};
int sum = sumblock(3, 4);
NSLog(@"sum=%d", sum);
// 示例2: 打印 n 条线
void (^printLinesBlock)(int, NSString *) = ^(int n, NSString *string) {
for (int i = 0; i < n; i++) {
NSLog(@"%@",string);
}
};
printLinesBlock(6, @"----------");
return 0;
}
声明 block 数据类型:
#import <Foundation/Foundation.h>
// 声明 block 数据类型
typedef int (^Sumblock)(int, int);
typedef int (^MinusBlock)(int, int);
// 无参数
void (^myblock)(void);
int main(int argc, const char * argv[]) {
__block int num1 = 20;
Sumblock sumblock = ^(int a, int b) {
// block 可以访问外部变量,但默认不能修改;
// 如果非要修改,可以在外部变量添加 __block 关键字。
NSLog(@"before num1=%d", num1);
num1 = 30;
NSLog(@"after num1=%d", num1);
return a + b;
};
int sum = sumblock(3, 4);
NSLog(@"sum=%d", sum);
MinusBlock minusBlock = ^(int a, int b) {
return a - b;
};
int minus = minusBlock(5, 1);
NSLog(@"minus=%d",minus);
return 0;
}
(六)protocol 协议
protocol 本质是用来声明方法,但不能声明成员变量。
只要某个类遵守了这个协议,就相当于拥有这个协议中的所有方法声明;而且在继承关系中,只要父类遵守了某个协议,那么相当于子类也遵守了。一般用于观察者模式、代理模式,以及团队间的开发规范等。
// MyProtocol2.h
#import <Foundation/Foundation.h>
// 遵守 <NSObject> 基协议 (推荐)
@protocol MyProtocol2 <NSObject>
- (void)test4;
- (void)test5;
@end
-------------------------------------------------------
// // MyProtocol.h
#import <Foundation/Foundation.h>
#import "MyProtocol2.h"
// 定义一个名叫 “MyProtocol” 的协议,并且继承自 MyProtocol2 协议
// 相当于同时拥有了 MyProtocol2 协议中的所有方法声明
@protocol MyProtocol <MyProtocol2>
// 声明几个方法
// 其中 @required:要求实现,不然有警告
@required
- (void)test1;
@required
- (void)test2;
// @optional:可以不实现,不会有警告
@optional
- (void)test3;
@end
-------------------------------------------------------
// Person.h
#import <Foundation/Foundation.h>
// 当然这里也可以像 @class 一样,
// 不用 #import,使用 @protocol MyProtocol; 提前声明。
#import "MyProtocol.h"
// Person 声明 <MyProtocol> 协议,
// 相当于拥有了 MyProtocol 里面的所有方法声明。
@interface Person : NSObject <MyProtocol>
// @interface Person : NSObject <MyProtocol,MyProtocol2>
@end
-------------------------------------------------------
// Person.m
#import "Person.h"
@implementation Person
- (void)test1 {
NSLog(@"test1-----");
}
- (void)test2 {
NSLog(@"test2-----");
}
- (void)test4 {
NSLog(@"test4-----");
}
- (void)test5 {
NSLog(@"test5-----");
}
- (void)dealloc {
NSLog(@"%@ 对象被释放",[self className]);
}
@end
-------------------------------------------------------
// test...
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
[p test1];
[p test2];
[p test5];
// 限制对象类型(缩小范围)
// NSObject<MyProtocol2> *obj = [[Person alloc] init];
id<MyProtocol2> obj = [[Person alloc] init];
[obj test4];
return 0;
}
protocol 在代理模式中的应用:
// 定义 TicketProtocol 协议
#import <Foundation/Foundation.h>
@protocol TicketProtocol <NSObject>
// 票价
@required
- (double) ticketPrice;
// 电影名
@required
- (NSString *) movieName;
// 影院名
@required
- (NSString *) cinemaName;
@end
-------------------------------------------------------
// TicketAgent1 遵守 TicketProtocol 协议
#import <Foundation/Foundation.h>
#import "TicketProtocol.h"
@interface TicketAgent1: NSObject <TicketProtocol>
@end
-------------------------------------------------------
// TicketAgent1 实现协议方法
#import "TicketAgent1.h"
@implementation TicketAgent1
- (double) ticketPrice {
return 68.5;
}
- (NSString *) movieName {
return @"Avatar";
}
- (NSString *) cinemaName {
return @"万达影城";
}
@end
-------------------------------------------------------
// TicketAgent2 遵守 TicketProtocol 协议
#import <Foundation/Foundation.h>
#import "TicketProtocol.h"
@interface TicketAgent2 : NSObject <TicketProtocol>
@end
-------------------------------------------------------
// TicketAgent2 实现协议方法
#import "TicketAgent2.h"
@implementation TicketAgent2
- (double) ticketPrice {
return 53.5;
}
- (NSString *) movieName {
return @"Resident Evil";
}
- (NSString *) cinemaName {
return @"百老汇影城";
}
@end
-------------------------------------------------------
// 定义目标类 Person
#import <Foundation/Foundation.h>
#import "TicketProtocol.h"
@interface Person : NSObject
// 代理模式:Person 持有 id 代理对象, 并遵守 TicketProtocol 协议
// 由于不清楚是哪个代理帮忙买票,所以使用万能指针 id。
@property (nonatomic,strong) id<TicketProtocol> agent;
// 通过代理帮忙买票,然后自己看电影
- (void)seeMovie;
@end
-------------------------------------------------------
#import "Person.h"
@implementation Person
- (void)seeMovie {
double price = [_agent ticketPrice];
NSString *movieName = [_agent movieName];
NSString *cinemaName = [_agent cinemaName];
NSLog(@"通过%@代理帮忙买到一张价格为%.2f的%@电影票去%@观看.",_agent,price,movieName,cinemaName);
}
- (void)dealloc {
NSLog(@"%@ 对象被释放", [self className]);
}
@end
-------------------------------------------------------
// test...
#import <Foundation/Foundation.h>
#import "Person.h"
#import "TicketAgent1.h"
#import "TicketAgent2.h"
int main(int argc, const char * argv[]) {
Person *person = [[Person alloc] init];
TicketAgent1 *agent1 = [[TicketAgent1 alloc] init];
person.agent = agent1;
// 一号代理帮忙买到一张价格为68.50的Avatar电影票去万达影城观看.
[person seeMovie];
TicketAgent2 *agent2 = [[TicketAgent2 alloc] init];
person.agent = agent2;
// 二号代理帮忙买到一张价格为53.50的Resident Evil电影票去百老汇影城观看.
[person seeMovie];
return 0;
}