知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。本文仅作为个人学习记录使用,欢迎在许可协议范围内转载或使用,请尊重版权并且保留原文链接,谢谢您的理解合作。如果您觉得本站对您能有帮助,您可以使用RSS方式订阅本站,这样您将能在第一时间获取本站信息。

最近项目时间比较紧,加上最近工作也有点忙,结果blog又开始长草了-,-。。。现在分享一下记下最近cocoa 开发时近遇到的一个小问题

遇到的问题

Protocol和Delegate是cocoa里面比较常用的设计模式,多用在对象之间进行数据交互,但是最近在我的项目中,发现,使用self.xxxdelegate = xxxdelegate时运行没有任何问题,但是在编译时会出现类型不匹配的警告

like this ↓

Delegate的警告

大家不用怀疑Obj1里面我肯定是实现了Obj1Delegate的,那为啥这里赋值的时候,却说类型不匹配呢,挺奇怪的啊。

问题分析

编译器肯定不会忽悠我们的,那肯定还是代码写的有问题,我们来看下代码吧(偷下懒,写在一起了)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//Obj1.h
@protocol Obj1Delegate <NSObject>

- (void)dosth;

@end

@interface Obj1 : NSObject

@property (weak) id<Obj1Delegate> delegate;
- (void)foo;

@end

//Obj1.m
#import "Obj1.h"

@implementation Obj1

- (void)foo
{
[self.delegate dosth];
}

@end

//Obj2.h
#import <Foundation/Foundation.h>

@interface Obj2 : NSObject

@end

//Obj2.m
#import "Obj2.h"
#import "Obj1.h"

@interface Obj2 () <Obj1Delegate>

@end

@implementation Obj2

- (void)dosth
{
NSLog(@"dosth");
}

@end

//使用者
#import "AppDelegate.h"
#import "Obj1.h"
#import "Obj2.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
Obj1 *o1 = [[Obj1 alloc] init];
Obj2 *o2 = [[Obj2 alloc] init];
o1.delegate = o2;
}

@end

很多童鞋肯定觉得,目测没问题啊。嗯嗯,确实是目测没问题,那么我们来想下为啥编译器会说不匹配呢?从编译器说不匹配的表象上来看,是编译器不知道Obj2实现了Obj1Delegate接口,为啥不知道呢。。。那就是我们的代码没有告诉他,那我们再来看关键的那部分代码,Obj2怎么实现的Obj1Delegate,以及我们怎么使用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//Obj2.m
#import "Obj2.h"
#import "Obj1.h"

@interface Obj2 () <Obj1Delegate> //在.m文件中实现了接口

@end

@implementation Obj2

- (void)dosth
{
NSLog(@"dosth");
}

@end

//使用者
#import "AppDelegate.h"
#import "Obj1.h"
#import "Obj2.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
Obj1 *o1 = [[Obj1 alloc] init];
Obj2 *o2 = [[Obj2 alloc] init];
o1.delegate = o2; //编译器不知道o2实现了Obj2Delegate
}

@end

感觉似乎找到问题了,我们在.m文件里面实现了接口,但是我们import的时候肯定是import的.h文件,而.h文件里面没有描述它实现了这个接口,所以,编译器在编译的时候就无法知道.m里面已经实现了,所以在这里他有一个警告,那我们对代码稍作修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Obj2.h
#import "Obj1.h"

@interface Obj2 : NSObject <Obj1Delegate> //实现的申明放到了这里

@end

//Obj2.m
#import "Obj2.h"

@interface Obj2 ()

@end

@implementation Obj2

- (void)dosth
{
NSLog(@"dosth");
}

@end

这样编译就不会有警告了,至此,问题解决了,但是有的童鞋又觉得奇怪了,那为啥一开始的时候编译器都不知道有这个,那么为啥程序还能正常run呢。

后面的疑问

为啥能正常run,这个应该是C语言编译和连接的问题了,目测Objc上也是这样的。看下面的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//main.c
int main()
{
int a = foo();
printf("%d\n", a);

return 0;
}

//foo.c
int foo()
{
return 8;
}

上面的代码,用gcc分别编译,之后连接,能够正常运行,并在屏幕上打印8,放到ide里面肯定警告曰:foo未找到之类的,但是却能正常run,这是为啥呢。C在编译和链接的时候,函数是在链接时才知道地址的,如果编译main.c的时候没有把foo.c编译得到的目标文件传递给gcc的话,链接器就会告诉我们找不到foo这个函数了。所以上面Objc也应该是类似的处理方式,在链接时,链接器才回去找obj2里面有没有dosht这个方法。

上面C语言的方式这个方式不是给大家开方便之门-,-在头文件中声明函数原型是非常好的习惯,这样可以让编译器帮你检查函数调用有没与问题,传入的参数是否正确等。

最后欢迎大家订阅我的微信公众号 Little Code

公众号

  • 公众号主要发一些开发相关的技术文章
  • 谈谈自己对技术的理解,经验
  • 也许会谈谈人生的感悟
  • 本人不是很高产,但是力求保证质量和原创