Archive for the ‘Cocoa开发’ Category.

NSString的内存管理 v.2011/12

之前写过一个NSString的内存管理

不过好像由于之前的理解不深刻,所以写的时候也没有说明太清楚。

CS193P的笔记上有如下实例:

aString = @"I am a string that 2 years old, man!";

这种情况下,字符串储存和管理由系统做,我们不用操心。

aString = [NSString stringWithFormat:@"I am a string that %d years old, man!",2];

第二种情况下,我们需要去retain和release这个字符串,系统不管。

这是之前写的内容。

为什么第一种我们不管这个产生的NSString的retain值呢?是因为其实由于我们建立了一个常量,无论我们retain还是release,对于这个对象都是没有意义的,大家可以尝试

NSString *foo = @"ssff";
[foo release];
[foo release];

会发现,这样的代码并不会让程序崩溃,而且无论你再release几次都是一样的。

第二种其实就是利用NSString 方法来生成一个NSString对象,这个时候我们要招呼到这个对象的retain值,否则就是内存溢出了。而我当时说的并不是一定要releaes或者retain这个stringWithFormat便捷方法产生的对象… 我语文不好,哈哈。便捷方法产生的都是autorelease的对象,谢谢大家的留言!

Mac: 基于事件的XML解析器(NSXMLParser)

XML用处很大… 用得很广… 最简单的来说用PHP和Javascript, JQuery都可以简单解析一个整齐的树结构XML文件

但是我们可爱的佛典网XML为复杂结构, 于是乎xslt帮不上什么忙了……我希望在新版的佛典中能包含所有的佛经, 于是简单并且高效的使用所有中华佛典网上的XML就是我的目标了.

于是,基于事件的XML解析器(Event-Driven XML Parser) NSXMLParser 就是我必须用到的了

继续前面的两篇Mac平台程序设计
Mac:打开一个目录
Mac:得到一个目录内的内容


1, 更改界面, 添加一个”解析一个目录里面的第一个的文件”的按钮

2, 构建XML解析部分

//1, 构建解析方法
-(void)parseXMLFile:(NSURL *)XMLURL //XMLURL为第一个文件的URL
{
	NSXMLParser *eventParser = [[NSXMLParser alloc] initWithContentsOfURL:XMLURL];
	[eventParser setDelegate:self];  //设置代理为本地
	[eventParser parse];  //开始解析
 
	NSLog(@"end Of parseXMLFile");
}
//2, 解析开始后,所有XML的内容由NSXMLParserDelegate处理
//  所以一定要注意在本地添加<NSXMLParserDelegate>到文件头
//在NSXMLParser读到<p>的时候重写一个<p>标签,原来的是XML中的,无法直接使用
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
	namespaceURI:(NSString *)namespaceURI 
	qualifiedName:(NSString *)qName 
	attributes:(NSDictionary *)attributeDict
{
	if ([elementName isEqualToString:@"p"]) {
		[htmlDataString appendFormat:@"<p>"];
		needOrNot = YES;  //我们需要<p>和</p>中间的内容,其余的在end中设置NO
		return;
	}
}
 
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
	if(needOrNot)
	{
		[htmlDataString appendString:string]; //如上,将需要的字段添加到得到的html结构String中
	}
}
 
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
	namespaceURI:(NSString *)namespaceURI 
	qualifiedName:(NSString *)qName
{
	if ([elementName isEqualToString:@"p"]) {
		[htmlDataString appendFormat:@"</p>"];
		needOrNot = NO;  //不需要的内容不允许上面的delegate去读取到htmlDataString中
		return;
	}
}
 
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
	NSLog(htmlDataString); //在结束后输出得到的html数据结构.
}

这样我就可以对佛典网的复杂结构xml开始分析了, 之后会放出更多对该xml处理的细节

Mac:得到一个目录内的内容

首先是打开一个目录

然后

1, 使用NSFileManager来得到这个目录的内容

NSArray *contentArray = [[NSFileManager defaultManager] 
	 contentsOfDirectoryAtURL:[[oPanel URLs] objectAtIndex:0] 
	//oPanel是上个帖子中的NSOpenPanel对象
	 includingPropertiesForKeys:[NSArray array]
	 options:0 
	 error:nil];
//我们得到一个Array的NSURL

2, 简单显示这个Array中的内容

	for(id innerUrl in contentArray)
	{
		NSLog([innerUrl absoluteString]);
	}

3, 结果

run
[Switching to process 3626]
Running…
2010-06-17 23:32:43.409 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0001.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0002.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0003.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0004.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0005.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0006.xml
2010-06-17 23:32:43.412 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0007.xml


*在简单输出含有空格的URL的String的时候会出现一定乱码, 如果有需要, 我会去分析为什么

Mac:打开一个目录

1, 获得一个打开窗口对象 NSOpenPanel

NSOpenPanel *oPanel = [NSOpenPanel openPanel]; //快捷建立方式不用释放, 我还记得, 你呢?

2, 设置这个对象的参数

	[oPanel setCanChooseDirectories:YES]; //可以打开目录
	[oPanel setCanChooseFiles:NO]; //不能打开文件(我需要处理一个目录内的所有文件)
	[oPanel setDirectory:NSHomeDirectory()]; //起始目录为Home

3, 处理得到的数据

	if ([oPanel runModal] == NSOKButton) {  //如果用户点OK
		NSLog([[[oPanel URLs] objectAtIndex:0] absoluteString]); 
		//我在console输出这个目录的地址
	}

4, 结果

[Switching to process 3330]
Running…
2010-06-17 22:49:02.583 XML2HTML[3330:a0f] file://localhost/Users/kingmtn/Music/

5, 简单的界面

只需要一个NSButton,并且将这个NSButton的缺省行为链接到我的对象方法-(IBAction)openDirectoryButtonClick:(id)sender上就可以了

*这是我XML2HTML工程的第一部分

用Icon Composer制作Mac/iPhone程序图标

上传过App Store的人还记得自己需要制作的各种尺寸的图标么?

制作Mac/iPhone的图标的工作就不用Firework/Photoshop了吧…

对!Apple的Developer文件夹不是白给的!(冷静…)

请大家找到Icon Composer,下面是路径~~

打开这个程序,不是很帅的程序,五个方块…

大家可以看到Mac程序中需要的各种尺寸的图标咯,怎么用呢?很简单拉~

拽一个图片进去就自动变成那个尺寸了,顺便说一下:试试png,别用jpg…

我知道这个长方的图标很2,我手头就是这个墙纸了…方格间可以随便拽~~

看到自己喜欢的尺寸了么?文件->保存成icon文件就可以咯!哇啦~~

makeKeyAndVisible

我不知道为什么,不过有人搜索makeKeyAndVisible方法。其实我这种懒人一般不会刨根问底,有些方法照打就好,那些白给的方法有些就别动就好了。

我们看看这个每个程序都有的方法吧:

[window makeKeyAndVisible];

由于iPhone是单窗口程序,所以也就只有这么一个Window对象,而且是UIWindow,不是NSWindow。而根据文档上所说:

“这个是便捷方法,去使被使用对象的主窗口显示到屏幕的最前端。你也可以使用hiddenUIView方法隐藏这个窗口”

所以基本上来说,对于编程者的区别仅仅在于在其前添加代码,或在其后添加代码。

在iPhone程序上读取PDF文件 (rev0.1 ^-^;)

老话,最近闹心的事情多,开心的事情少,但是正事还是要干的。学习得需要,努力的需要,钱的需要… … 天上给我下点儿钞票吧!


言归正传,怎么在iPhone程序中读取PDF的内容呢?答案是,苹果为我们准备了一个很神奇的framework Q2D(Quartz 2D)。Q2D提供了全套的PDF读取API,接下来我们来看看如果简单的使用Q2D来读取PDF文件:

我建立了一个工程叫iPhonePDF, 添加了一个UIScrollView(不知道怎么添加UIScrollView? 添加一个UIView然后把interface上的UIView改成UIScrollView就可以啦…)名为PDFView

看看PDFView里面有什么吧

@interface PDFView : UIScrollView {
 
	NSString *filePath;
	CGPDFDocumentRef pdfDocument;
	CGPDFPageRef page;
	int pageNumber;
}
 
@property (copy, nonatomic) NSString *filePath;
@property int pageNumber;
 
-(CGPDFDocumentRef)MyGetPDFDocumentRef;
-(void)reloadView;
-(IBAction)goUpPage:(id)sender;
-(IBAction)goDownPage:(id)sender;
@end

filePath是储存pdf文件的位置的,得到文件位置就是老话题了:[NSBundle mainBundle]… 后面的会写吧… 不记得了在我博客里面搜索吧

CGPDFDocumentRef是PDF文档索引文件,Q2D是Core Foundation的API,所以没看到那个星星~

CGPDFPageRef是PDF页面索引文件

pageNumber是页码

下面的几个函数其实一看就明了了,翻页的,和刷新页面的。第一个是自定义的getter

然后我们看看m文件里面有用的方法:

@implementation PDFView
@synthesize filePath,pageNumber;
 
- (void)drawRect:(CGRect)rect //只要是UIView都有的绘图函数,基础哟~
{
	if(filePath == nil) //如果没被初始化的话,就初始化
	{
		pageNumber = 10;   //这个其实应该由外部函数控制,不过谁让这个程序特别简单呢
		filePath = [[NSBundle mainBundle] pathForResource:@"zhaomu" ofType:@"pdf"];
                //这里,文件在这里!
		pdfDocument = [self MyGetPDFDocumentRef]; //从自定义getter得到文件索引
	}
 
	CGContextRef myContext = UIGraphicsGetCurrentContext();
        //这个我研究了一段时间呢,不过就照打就可以了
 
	page = CGPDFDocumentGetPage(pdfDocument, pageNumber);
        //便捷函数,告诉人家文档,告诉人家页码,就给你页面索引
 
	CGContextDrawPDFPage(myContext, page);
        //画!
}
 
//此getter可以考虑照打... 都是CF函数,我看到就恶心。
//其实不是很难了,得到文件,转换成URL,然后通过
//CGPDFDocumentCreateWithURL(url)得到文件内容索引
//Ta Daaa~~
- (CGPDFDocumentRef)MyGetPDFDocumentRef
{
	CFStringRef path;
	CFURLRef url;
	CGPDFDocumentRef document;
	path = CFStringCreateWithCString(NULL, [filePath UTF8String], kCFStringEncodingUTF8);
	url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, 0);
	CFRelease(path);
	document = CGPDFDocumentCreateWithURL(url);
	CFRelease(url);
	return document;
}
 
-(void)reloadView
{
	[self setNeedsDisplay]; //每次需要重画视图了,就call这个
}
 
-(IBAction)goUpPage:(id)sender
{
	pageNumber++;
 
	[self reloadView];
}
-(IBAction)goDownPage:(id)sender
{
	pageNumber--;
	[self reloadView];
}
@end

我这个是个少半成品的程序呢… 我当时弄了几个PDF测试程序,但是找不到了。不过意思是一样的,剩下的东西由大家来发挥吧,有问题记得留言噢!我有问必答!

循环使用整个NSArray内的对象

循环使用整个NSArray内的对象是非常常用的了,而且最近我在研究究竟怎么能方便的把NSArray存入Core Data,所以这更是必要了,看看如下的方法吧:

1,Objective-C 2.0法,最应该使用的

NSArray *aArray; //我们的Array,假设他已经初始化有内容了
 
for(id innerObj in aArray) //id可以由其他对象类型替代
{
//也就是说, 在循环中的参数innerObj就是aArray中的对象
//由于NSArray中只能储存对象,所以我们使用id作为内涵对象的类型,其实就是个指针了
//而且如果NSArray中的内容不一致,id更不会出现冲突
}

2,C的老方法,不推荐,低性能

NSArray *aArray; //我们的Array,假设他已经初始化有内容了
 
for(int i=0;i<[aArray count]; i++)
{
[[aArray objectAtIndex:i] 然后作你想作的事情]; //做爱 作的事情...
}

3, 用NSEnumerator(不知道怎么翻译阿….)

 NSEnumerator* myIterator = [myArray reverseObjectEnumerator];
 id anObject;
 
 while( anObject = [myIterator nextObject]) //每次读取“逐读器”的下一个对象
 {
     //anObject和刚才的innerObj是一个意思,但是看看多了这么多行
 }

结语:
就用第一种方法吧….

答问留言:关于NSOperation

1,operationQueue 里边应该可以同时添加多个operation吧?

是的,本来operationQueue的目的就是多线程管理,那多线程,可只是一个线程。

而且我们可以设置这个队列每次被处理的“操作”数量

	NSOperationQueue *aQ = [[NSOperationQueue alloc] init];
	[aQ setMaxConcurrentOperationCount:10];

这里的setMaxConcurrentOperationCount就是同时被处理的“操作数”,参数为整数int或NSInteger (两个是一样的,不记得的可以在我的博客里面搜索一下噢~)

2,那main函数应该怎么写?

main函数中其实只需要写你要在另外一个进程里面作的事情。比如对于我来说,我常常只是作一个简单的事情,那我会用NSInvocationOperation,NSOperation的简化版。比如说:

NSInvocationOperation *aOpt = [[NSInvocationOperation alloc]
		 initWithTarget:self selector:@selector(doSomeThing) object:nil];
 
- (void)doSomeThing
{
    //读取大量大延迟数据等等
    //可以使用performSelectorOnMainThread来将得来的数据返回到主线程
}

在doSomeThing函数里面,我可以从网上读取一些东西,但是读取是需要占用时间,而堵塞主线程的。而使用NSOperation这样使用就不会了。

而如果是NSOperation,虽然复杂了一些,又是做一个NSOperation的子类。其实main函数做得事情和doSomeThing是一抹一样的。只不过如果你制作这个子类,你对其操作的内容可以更多,可以制作更复杂的读取,载入操作等等,而且你可以重复使用这个类功能阿。再者,NSOperation也提供了对runtime操作的支持,不过那就太麻烦了,一般不大用的上。


最近麻烦事情真是太多了,而且还都不顺利,死的心都有阿。还好有这么多好朋友支持我,我这个博客也一点儿一点儿的有回复了…大家别那么冷漠,管我写的好还是不好,按东北话来说“吱一声”嘛~唉,我要加油阿!我还有那么多的事情没办到呢,怎么能放弃!他妈的!!!!!阿!!!!!!

从0开始Core Data(1)

大家都有Snow Leopard和XCode 3.2了吧?会发现XCode变了不少,帅拉!

屏幕快照 2009-09-01 上午11.58.22

那我们点击“Create a new Xcode project”来开始我们的Core Data程序工程吧!

屏幕快照 2009-09-01 下午12.06.52

在这里,XCode已经给我们准备好了一个几乎可以直接去用的程序模板,直接选择“基于导航的应用程序”,然后记得点选“Use Core Data for storage”

屏幕快照 2009-09-01 下午12.14.03

建立好的程序名为XCDtest01,我们尝试编译运行,看看得到的程序是什么样的:

屏幕快照 2009-09-01 下午12.16.47

基本上来说,这个程序把什么都给我们弄好了,一个程序,在点击添加按钮之后添加现在的时间标签,如果点击“Edit”编辑按钮,就进入编辑模式,可以删除任意条信息。

如果点开xcdatamodel文件,我们可以看到,其中的实体为Event,有一个参数,名为“时间标签”。也就是说,如果我们想对这个列表视图中的数据作更改,只需要适当更改这个实体的参数,并配置- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 代理条目就可以显示适当的内容了。

屏幕快照 2009-09-01 下午12.20.19

这个预置程序与我们之前讨论的程序不同的,在于这个程序中并没有配置一个可变数组(NSMutableArray)来作数据副本,以达到高速存储的目的。因为Core Data在向磁盘和内存操作,NSMutableArray是内存操作,速度上比较有优势。但是我们有的这个小程序太小了,再者苹果有可能认为那种方法是非标准的。所以在这里才没有那么作

NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:@"timeStamp"] description];

在这个程序的cell配置代理里面,仅仅是从“获取操作控制器”中得到了“被管理对象”,然后再读取其中的内容到cell的text中。

希望大家也使用这个模板,自己创建一个Core Data程序,然后简单的理解一下代码。下次我们聊只使用基于Window的程序模板+Core Data来创建程序