Objective-C中的协议

一.问题的产生


NetworkConnector类提供所有和网络服务器的交互,包括连接、断开连接和手法数据。BusinessLogic类会接收NetworkConnector收到的数据,并确定它的走向。
网络连接是一个在其他应用或同一个应用的其他地方一定会复用的东西。所以必须将它与BusinessLogic类设计成没有紧密耦合。

C++等语言使用多继承来解决这类问题,强制BusinessLogic类继承自NetworkConnector可以依赖的类。但是多继承会产生新的问题,即砖石问题。如果D类同时继承自B类和C类,同时B、C也继承自共同的超类A,这种情况下,如果D没有重写A中的方法,当D调用A类的方法的时候,无法确定会调用哪一个父类的方法B类的还是C类的?
Objective-C只能使用单继承。

二.Objective-C的解决方案

Objective-C使用协议来声明一个接口来解决这个问题。
接口是在不提供默认实现时由一个类来具体实现,它只提供声明这些方法的接口机制,让可复用组件不依赖于特定的类来实现,而依赖于以协议形式存在的接口。

协议的声明

@protocol NetworkClient
@required
-(void) networkConnector:(NetworkConnector *) in gotData:(NSData *) data;
@optional
-(void) networkConnectorDisconnected:(NetworkConnector *) in;
@end

@required关键字表明其后的方法是该协议必须实现的方法。
@optional关键字表明其后的方法是实现类可以选择性实现的方法。
同时,协议不能有成员变量。

声明一个必须实现协议的对象

id<NerworkClient> *delegate; //<>中是该对象必须实现的协议

通常使用id数据类型来声明实现给定协议的实例变量。编译器会通过<>中的协议类型来确认协议的必须按方法是否在该对象上实现了。

非正式协议

它是在Cocoa和Objective-C中的旧协议,通常是NSObject类的类别。

正式协议

正式协议可以提供更好的类型安全。用@protocol关键字来声明。有@required和@optional关键字来保证必须实现的和可选的方法。

避免协议循环依赖

协议在各自的声明中可以引用另一个协议。

@protocol Foo
-(void)someMethodRequiringBar:(id<Bar>)inBar;
@end

如果Bar也需要Foo协议的话,就会导致协议之间的循环依赖,发生编译器错误。

@protocol Bar
-(void)someMethodRequiringFoo:(id<Foo>)inFoo;
@end

解决方法,在Bar.h中加入@protocol Foo,告诉编译器Foo是一个协议,不用导入Foo.h。

2016/4/17 23:12 下午 posted in  Objective C

Objective-C中的类别

刚刚翻看了OC的书中关于类别的一章,现将OC中类别的使用和限制总结如下。

定义及使用

1.通过在类上声明和实现方法来扩展现有类的功能,且不需要访问要扩展类的原有代码。
2.类别不是子类,添加的方法可以直接在操作的类中使用,因此可以避免通过继承的方式添加某些方法的实现。
3.可以通过类别重写现有方法,但一般不推荐,这样会导致无法调用被扩展的类的初始方法,只能访问重写过的方法。

声明

头文件

#import <Foundation/Foundation.h>

@interface NSMutableString (GUID) //括号里为类别的名字

-(void) appendGUID;
+(id) stringWithGUID;
@end

实现

#import "NSMutableString+GUID.h"

@implementation NSMutableString (GUID)

-(void) appendGUID{
    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
    NSString *string = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid));
    [self appendString:string];
   // CFRelease(uuid);
}

+(id) stringWithGUID{
    NSMutableString *string = [self string];
    [string appendGUID];
    return string;
}

@end

扩展成员方法:可访问类的所有成员变量、可以通过self调用类的其它方法、可以使用super关键字调用父类的方法。self关键字指实例对象。
扩展类方法:无法访问成员变量。self关键字指类对象。

使用

#import <Foundation/Foundation.h>
#import "NSMutableString+GUID.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableString *str = [NSMutableString stringWithGUID];
        NSLog(@"GUID=%@", str);
    }
    return 0;
}

限制

1.类别不能在扩展类中添加任何成员变量。
2.重写现有方法时,无法调用原始现有对象方法。
3.若两个类别都定义了一个相同类的相同方法,运行时实际无法确定调用哪一个。

2016/4/6 0:9 上午 posted in  Objective C

二维数组作为参数在C语言函数中如何传递

今天做了几道处理字符串的面试题,发现自己已经忘了二数组如何在C语言函数中作为参数传递。
现在把方法整理出来,以备能很快地回忆起来。

void printArray(int a[3][2]);
void printArray(int a[][2]);
void printArray(int (*a)[2]);

以上三种方式都可以将二维数组传入函数中,其中int a[][2]是int (*a)[2]的语法糖。


#include<stdio.h>
void printArray(int a[][2]){
        for(int i=0; i<3; ++i){
                for(int j=0; j<2; ++j){
                        printf("%d\n", a[i][j]);
                }
        }
}
int main(){
        int test[][2] = {{8, 21}, {43, 54}, {89,23}};
        printArray(test);
        return 1;
}

手工寻址

#include<stdio.h>
void printArray(int **a, int m, int n){
        for(int i=0; i<m; ++i){
                for(int j=0; j<n; ++j){
                        printf("%d\n", *((int*)a+i*n+j));
                }
        }
}
int main(){
        int test[][2] = {{8, 21}, {43, 54}, {89,23}};
        printArray((int**)test, 3, 2);
        return 1;
}

int **a;

*a表明a是一个指针,*(*a)表明a指针指向的内存单元(*a)的内容存放的也是一个指针。

2016/4/3 1:18 上午 posted in  C/C++