iOS 应用开发

iOS 应用开发(三)OC 面向对象之三大特性

2022-12-15  本文已影响0人  HiCloudKK

面向对象的三大特性:封装、继承和多态。

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;
}
上一篇 下一篇

猜你喜欢

热点阅读