iOS -- 开发技巧(三)

IOS开发经验总结

转自:http://www.cocoachina.com/bbs/read.php?tid=105689

(一)关于UITableView
1.任意设置Cell选中状态的背景色:

UIView *bgView = [[UIView alloc] init]; 
bgView.backgroundColor = [UIColor orangeColor]; 
self.selectedBackgroundView = bgView; 
[bgView release];

该方法设置的是纯色, 也可以使用任何图片,把selectedBackgroundView设成UIImageView。

2.如果Table中有控件,这里以switch为例(适合其它可修改值的各种控件),要在switch的UIControlEventValueChanged事件的处理方法里把值记录下来。以下方法是不可取的:在执行的最后把所有cell遍历一遍,处理各控件的值。因为没显示出来的cell,是取不到的,当然也就取不到该cell里的控件。所以正确的做法是,在控件可见时,如果值变了,立即处理。当然,如果你的Cell少,不会出现隐藏的情况就随便了。

3.方法flashScrollIndicators:这个很有用,闪一下滚动条,暗示是否有可滚动的内容。可以在ViewDidAppear或[table reload]之后调用。

(二)设置线宽,如果是retina屏,lineWidth设为1,实际显示的宽度是2个像素,这里进行一下处理:

#define SETLINEWIDTH(ctx,w) CGContextSetLineWidth(ctx, w/[UIScreen mainScreen].scale)

(三)_cmd:表示该方法的selector,可以赋值给SEL类型的变量,可以做为参数传递。
例如一个显示消息的方法:

-(void)ShowNotifyWithString:(NSString *)notifyString fromMethod:(SEL) originalMethod;

originalMethod就是调用这个方法的selector。

调用:

NSString *stmp = @"test"; 
[self ShowNotifyWithString:stmp fromMethod:_cmd];

如何记录当前方法名称:

NSLog(NSStringFromSelector(_cmd));

(四)在CGContext中输出汉字:CGContextShowTextAtPoint是不支持汉字的,需要用NSString的drawAtPoint或drawInRect方法

(五)一个不停震动的方法:
// 定义一个回调函数,震动结束时再次发出震动

void MyAudioServicesSystemSoundCompletionProc (SystemSoundID  ssID,void *clientData) 

      BOOL* iShouldKeepBuzzing = clientData; 
      if (*iShouldKeepBuzzing) {        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); 
      } else { 
           //Unregister, so we don't get called again... 
           AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate); 
      }  
}

以下为调用的代码:

BOOL iShouldKeepBuzzing = YES; 
AudioServicesAddSystemSoundCompletion (                                                                                    
  kSystemSoundID_Vibrate,                                                                       
  NULL,                                                                                                    
  NULL,                                                                                                              
  MyAudioServicesSystemSoundCompletionProc,                                                 
  &iShouldKeepBuzzing ); 
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);

(六)关于更新,iPhone自动保存document中的内容,如果你把文件放在document中,以后开发又改了这个文件的内容或格式,那更新之后运行很可能出错。解决的办法是,配置文件放bundle里,或者改个文件名。每次更新前都要从App store 下载旧版本,运行一段一时间后,再此基础上编译新版,运行不出错才能上传

(七)初学者或者不小心容易犯的错误:在dealloc里要调用[super dealloc],千万不要调用[super release]
(八)需要调试的类最好重写description,输出重要变量的值,因为调试窗口variableView有时候变量值显示不出来。
(九)去掉app图标的发光效果:info.plist里增加Icon already includes gloss effects,值设为YES
(十)写代码时字符串太长 怎么换行:

NSString *string = @"ABCDEFGHIJKL" \ 
                                        "MNOPQRSTUVsWXYZ";

(十一)UIImage:stretchableImageWithLeftCapWidth:topCapHeight: 有时图片模糊(blur)的原因:像素没有和device pixel对齐.使用instrument 的Core Animation可以检测这个,勾选”color misaligned images”,如果图片显示为红紫色,就是没有对齐

(十二)获取沙盒路径

NSHomeDirectory();

NSString *path = NSHomeDirectory();
上面的代码得到的是应用程序目录的路径,在该目录下有三个文件夹:Documents、Library、temp以及一个.app包!
该目录下就是应用程序的沙盒,应用程序只能访问该目录下的文件夹!!!

请参考下面的例子:
1、
NSString *path1 = NSHomeDirectory();

NSLog(@"path1:%@", path1);
path1:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830
2、
NSString *path2 = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

NSLog(@"path2:%@", path2);
path2:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830/Library/Caches
3、
NSString *path3 = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSLog(@"path3:%@", path3);
path3:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830/Documents
4、
NSString *path4 = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

NSLog(@"path4:%@", path4);
path4:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830/Documents
5、
NSString *path5 = [NSHomeDirectory() stringByAppendingPathComponent:@"Library"];

NSLog(@"path5:%@", path5);
path5:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830/Library
6、
NSString *path6 = [NSHomeDirectory() stringByAppendingPathComponent:@"temp"];

NSLog(@"path6:%@", path6);
path6:/Users/yuanjun/Library/Application Support/iPhone Simulator/4.2/Applications/172DB70A-145B-4575-A31E-D501AC6EA830/temp


(十三)块语法,循环引用

__block typeof(self)bself = self;  是什么意思?

是把当前的self 变成一个 block形式的指针吗?
但变成block的指针 又和原来self有那些不同呢?
希望高手指点

shichangone2012-09-14 18:35
防止在block中用到self时把self对象retain, 造成内存泄露。

joymoo2012-09-15 08:57
block对于其变量都会形成strong reference,对于self也会形成strong reference ,而如果self本身对block也是 strong reference 的话,就会形成 strong reference 循环,造成内存泄露,为了防止这种情况发生,在block外部应该创建一个week(__block) reference。

所以在block内如果有self的话,一般都会在block外面加一句_block typeof(self)bself = self;

__block typeof(self) bself = self;  
  
[self methodThatTakesABlock:^ {  
  
    [bself doSomething];  
}

(十四)一个类中开辟线程使用nstimer,不调用dealloc(彪哥)

_refreshTimer  = [[NSTimerscheduledTimerWithTimeInterval:1.0target:selfselector:@selector(shuaUI)userInfo:nilrepeats:YES]retain];

    [[NSRunLoopcurrentRunLoop]addTimer:_refreshTimerforMode:NSRunLoopCommonModes];关键在这

    如果要刷界面(界面都在主线程中)就要回到主线程

 [selfperformSelectorOnMainThread:@selector(shuaLabel:)withObject:dicwaitUntilDone:NO];

延时执行方法

[selfperformSelector:@selector(doneLoadingTableViewData)withObject:nilafterDelay:1.0];

//    NSTimer * timer;

//    NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:1.0];

//    timer = [[NSTimer alloc] initWithFireDate:fireDate interval:1.0f target:self selector:@selector(shuaUI) userInfo:nil repeats:YES];

//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

//    self.refreshTimer = timer;

//    [timer release];

 

(十五)不能调用dealloc方法

循环引用容易照成不调用dealloc

当一个实例的retainCount不为0时,不会调用他的dealloc方法。

例如:

FinanceViewController *financeVC = [[FinanceViewController alloc] init];

[self pushViewController:financeVC withData:nil animate:YES];

//[financeVC release];

如果financeVC没有在这个时候release,当从financeVC pop回来的时候不会调用financeVC的dealloc方法,对于add上的view,如果没有removeFromSuperView的话,也不会调用该view的dealloc方法,造成内存泄露!


(十六) performSelector延时调用导致的内存泄露

前几天在给游戏做收尾测试时,发现了一个关于内存泄露的问题,一直没找着问题所在,经过反复调试和查找资料今天终于解决了,特此记录下来以免以后再犯!

关于objective-c的内存管理,我们都知道一个原则就是“谁创建,谁释放”,换句话说,不是我们创建的,就不用我们去释放。但是实际上objective-c的内存管理远远没那么简单,我的情况是这样的:

我在debug模式下面用CCLOG在dealloc函数里面输出一些信息,目的就是要检查场景的dealloc方法在replaceScene的 时候有没有被调用,按照子龙山人大哥的说法,如果场景切换的时候dealloc没有调用,说明你这个场景的内存有问题。有可能被某个对象retain了, 其retainCount在replaceScene的时候没有减少到0,所以dealloc方法是不会调用的。如果dealloc方法都没有调掉,那么 这其实就是一种内存泄露。我在检查时,发现一个场景死活不调用dealloc,最后恨不得把所有的游戏逻辑都移除了,还是不走dealloc。

最后的最后才发现实际上是performSelector延时调用的问题,经查找资料,performSelector关于内存管理的执行原理是这 样的执行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的时候,系统会将tableLayer的引用计数加1,执行完这个方法时,还会将tableLayer的引用计数减1,而在我的游戏里这个延时执行函数是 被多次调用的,有时切换场景时延时函数已经被调用但还没有执行,这时tableLayer的引用计数没有减少到0,也就导致了切换场景dealloc方法 没有被调用,出现了内存泄露。

所以最后我的解决办法就是取消那些还没有来得及执行的延时函数,代码很简单:

[NSObject cancelPreviousPerformRequestsWithTarget:self]

当然你也可以一个一个得这样用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]

加上了这个以后,切换场景也就很顺利地执行了dealloc方法,至此问题解决!

 

最后在找资料时也发现了,延时调用实现长按钮的实现思路,记录下来以备后用:

在touchBegan里面

[self performSelector:@selector(longPressMethod:) withObject:nil afterDelay:longPressTime]

然后在end 或cancel里做判断,如果时间不够长按的时间调用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(longPressMethod:) object:nil]

取消began里的方法

 

最后最后总结:

performSelector是一个很有用的函数,跟它打过不少交道,经过血与泪的教训,总结一下它的使用如下:

使用前先检测一下,

SEL testSelector = @selector(test:);   

 if([tester respondsToSelector:testSelector])  

  {  

          //如果响应就执行

          [tester test:@"invoke test method"];  

  }

使用后,如果有必要,需要显示的调用cancelPreviousPerformRequestsWithTarget:selector:object: ,否则有可能产生内存泄露,而且这种内存泄露很难发现,因为它并不违反任何规则,所以一定要注意!


(十七) @selector可以叫做选择器
selector可以叫做选择器,其实指的就是对象的方法,也可以理解为C语言里面的函数指针,在面向对象里面的对应概念。

@selector(xxxx)的作用是找到名字为xxxx的方法。一般用于[a performSelector:@selector(b)];就是说去调用a对象的b方法,和[a b];的意思一样,但是这样更加动态一些。@selector(xxxx)返回的类型是SEL,看方法说明的时候如果参数类型是SEL,那么就是要接受@selector(xxxx)返回的值的那种了。

(十八) 输出坐标frame

NSLog(@"dv===%@",NSStringFromCGRect(dv.frame));


(十九) 计时器NSTimer

计时器NSTimer 方法使用

(本题目1秒钟做一次英雄攻击怪兽m即attackMonster:m共做5次)

 千峰视频第7讲里面有计时器

 第一种方法 

-(void)initTimer{

[NSTimerscheduledTimerWithTimeInterval:1target:selfselector:@selector(do1:)userInfo:nilrepeats:YES];

}     参数解释:1为时间间隔 do1:是要调用的方法  nil是参数 yes是是否执行

第二种方法-(void)initTimer

{

 NSTimer  *timer;

  timer = [NSTimerscheduledTimerWithTimeInterval:1.0

                                                           target:self

                                                         selector:@selector(handleTimer:)

                                                         userInfo:nil

                                                          repeats:YES];

       [[NSRunLoopcurrentRunLoop ]addTimer:timerforMode:NSRunLoopCommonModes];

       int i=5;

       while (i>0) {

        [[NSRunLoopcurrentRunLoop]runUntilDate:[NSDatedateWithTimeIntervalSinceNow:1.0]];

        i--;

    }

    [timer invalidate];移除timer

}


(二十)  UIGraphicsGetCurrentContext 当前上下文

想在initWithFrame或者其他函数里调用UIGraphicsGetCurrentContext()函数,

结果被告知Invalid Context,查资料。

按照文档中的说法,系统会维护一个CGContextRef的栈,而UIGraphicsGetCurrentContext()会取栈顶的CGContextRef,

正确的做法是只在drawRect里调用UIGraphicsGetCurrentContext(),

因为在drawRect之前,系统会往栈里面压入一个valid的CGContextRef,

除非自己去维护一个CGContextRef,否则不应该在其他地方取CGContextRef。

终于,恍然大悟


(二十一) 自动匹配iphone4或者iphone5屏幕大小 

#define iPhone5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(640, 1136), [[UIScreen mainScreen] currentMode].size) : NO)
#define DIPHONE4 CGRectMake(0, 0, 320, 460)
#define DIPHONE5 CGRectMake(0, 0, 640, 1136)


(二十二)NS、CF、CA、CG、UI***

在IOS开发中,经常会遇到NS开头的对象,这个要从乔帮主历史恩怨说起。当年Steve Jobs 和John Scullery与恩怨,乔帮主当年被人挤兑出苹果,自立门户的时候做了个公司叫做NextStep,里面这一整套开发包很是让一些科学家们喜欢,而现在Mac OS用的就是NextStep这一套函数库。

   这些开发NextStep的人们比较自恋地把函数库里面所有的类都用NextStep的缩写打头命名,也就是NS****了。

你还可以看到其他名字打头的一些类,比如CF、CA、CG、UI等等,比如

CFStringTokenizer 这是个分词的东东

CALayer 这表示Core Animation的层

CGPoint 这表示一个点

UIImage 这表示iPhone里面的图片 

CF说的是Core Foundation,CA说的是Core Animation,CG说的是Core Graphics,UI说的是iPhone的User Interface……还有很多别的,等你自己去发掘了。



(二十二)

OneV‘s Den在博客里出了10道iOS面试题,用他的话是:"列出了十个应聘Leader级别的高级Cocoa/CocoaTouch开发工程师所应该掌握和理解的技术" 。 
在这里給一份我的答案。  
1. 你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?  
Objecitve-C的重要特性是Runtime(运行时),在#import <objc/runtime.h> 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用  
```objective-c
Method method1 = class_getInstanceMethod(cls, sel1);
Method method2 = class_getInstanceMethod(cls, sel2);
method_exchangeImplementations(method1, method2);  
```   
代码交换两个方法,在写unit test时使用到。  
2. 你实现过多线程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在线程中创建或者传递?你是用什么样的策略来实现的?  
没实现过多线程的CoreData(待实践)
http://blog.csdn.net/chen505358119/article/details/9344389
http://www.cnblogs.com/rolandash/p/3769127.html
<!--more--> 
3. Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG做过些什么动画或者图像上的内容。(有需要的话还可以涉及Quartz的一些内容)  
UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。    
UIKit | 
------------ | 
Core Animation | 
Core Graphics |
Graphics Hardware|  
使用CA做过menu菜单的展开收起(太逊了)  
4. 是否使用过CoreText或者CoreImage等?如果使用过,请谈谈你使用CoreText或者CoreImage的体验。
CoreText可以解决复杂文字内容排版问题。CoreImage可以处理图片,为其添加各种效果。体验是很强大,挺复杂的。
5. NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了…)
NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个通过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,可以使用NSNotification;如果是只需要观察某个对象的某个属性,可以使用KVO。
对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,往往是一对多的关系。委托模式,从技术上可以现在改变delegate指向的对象,但不建议这样做,会让人迷惑,如果一个delegate对象不断改变,指向不同的对象。  
6. 你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。
使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
更详细的答案见我的这篇文章
7. 既然提到GCD,那么问一下在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?
使用block是要注意,若将block做函数参数时,需要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是能够读取其他函数内部变量的函数。 更详细的答案见我的这篇文章
8. 您是否做过异步的网络处理和通讯方面的工作?如果有,能具体介绍一些实现策略么?
使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,详细可见开源框架[LWConnetion](https://github.com/xunyn/LWConnetionDemo)。  
9. 对于Objective-C,你认为它最大的优点和最大的不足是什么?对于不足之处,现在有没有可用的方法绕过这些不足来实现需求。如果可以的话,你有没有考虑或者实践过重新实现OC的一些功能,如果有,具体会如何做?
最大的优点是它的运行时特性,不足是没有命名空间,对于命名冲突,可以使用长命名法或特殊前缀解决,如果是引入的第三方库之间的命名冲突,可以使用link命令及flag解决冲突。  
10. 你实现过一个框架或者库以供别人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。
抽象和封装,方便使用。首先是对问题有充分的了解,比如构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操作,并且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,通过抽象让其更为健壮、便于更改。其次是API的说明文档。

(二十三)ios5的崩溃是因为广告框架的引用。改为弱引用


(二十四)block设成属性,循环引用不释放。


(二十五)命名冲突,一般是引用第三方类库引起的,去掉.m文件的强引用或者使用link命令及flag解决冲突


(二十六)单例的代理设成self,在释放的时候一定要将代理置nil。搜房帮代码

[SouFunSensitiveWordsFilter sharedSensitiveWordsFilter].delegate=self;

dealloc里面[SouFunSensitiveWordsFilter sharedSensitiveWordsFilter].delegate=nil;








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值