1.å¦ä½ä½¿ç¨NSFetchedResultsController
2.Delphi 做游戏热键(HotKey)的源码一个问题!~
3.RTL级综合是源码什么意思?
4.xcode调试心得
5.å¦ä½å¼åä¸ä¸ª AVPlayer iPad ç¨åº
å¦ä½ä½¿ç¨NSFetchedResultsController
ä½ æ¯ä¸æ¯å·²ç»å¿è®°äºä»¥å讲è¿ä»ä¹å¢ï¼æ们æ¥å¤ä¹ ä¸ä¸ï¼å¨ç¬¬ä¸ç¯æç¨ä¸ï¼ãiOSæç¨ï¼Core Dataæ°æ®æä¹ æ§åå¨åºç¡æç¨ãä¸æ们讲äºå¦ä½ä¸ºä¸ä¸ªiOSç¨åºå建ä¸ä¸ªCore Dataçæ°æ®æ¨¡ååæµè¯çæ¹æ³ï¼è¿ææ们è¿æè¿ä¸ªæ°æ®æ¨¡åä½ä¸ºæ°æ®æºè¿æ¥å°äºä¸ä¸ªè¡¨è§å¾ä¸ã å¨ç¬¬äºç¯æç¨ï¼ãiOSæç¨ï¼å¦ä½ä½¿ç¨Core Data â é¢å è½½åå¼å ¥æ°æ®ãï¼æ们讲äºå¦ä½è§£æä¸åæ ¼å¼çæ°æ®æ件å°ä¸ä¸ªCore Dataéç¨çSQliteæ°æ®åºä¸ï¼è¿æå¦ä½å°è¿ä¸ªæ°æ®åºç§»æ¤å°æ们çiOS项ç®ä¸å»ï¼å¥½è®©æ们çåºç¨æä¸äºåå§æ°æ®ã ä½ å¯ä»¥ä»è¿éä¸è½½ç¬¬äºé¨åçæºç ã为ä»ä¹è¦ä½¿ç¨ NSFetchedResultsControllerï¼ å°ç®å为æ¢ï¼æ们就åå¨ä½¿ç¨SQLite3çæ¹æ³ä¸æ ·ï¼å 为æ¬è´¨ä¸Core Dataå°±æ¯å¨æä½SQLiteæ°æ®åºï¼ä½æ¯æ们åç代ç æ¯ç´æ¥ä½¿ç¨SQLiteæ´å°ï¼æ们使ç¨åç§æ°æ®åºåè½ä¹æ´å®¹æã ä½æ¯ï¼æ们è¿æä¸ä¸ªå¾å¥½ç¨çCore Dataç¹æ§æ²¡æç¨ä¸ï¼è¿ä¸ªç¹æ§è½å¤å¾å¤§ç¨åº¦ä¸çæé«æ们ç¨åºçæ§è½ï¼ä»å°±æ¯ï¼NSFetchedResultsControllerãç°å¨å¨æ们çä¾åç¨åºä¸ï¼æ们é½æ¯ä¸ä¸åå°ææçæ°æ®å ¨é¨å è½½è¿è§å¾ï¼å¯¹äºæ们çè¿ä¸ªåºç¨èè¨ï¼è¿ä¹è®¸æ¯å¯ä»¥æ¥åçï¼ä½æ¯å¦æä¸ä¸ªåºç¨æ大éçæ°æ®ï¼è½½å ¥é度就ä¼åå¾å¾æ ¢ï¼ä¹ä¼ç»ç¨æ·ä½éªé æå½±åã å¨çæ³çæ åµä¸ï¼æ们åªè½½å ¥ç¨æ·æ£å¨æµè§çé£ä¸é¨åçæ°æ®ï¼å¹¸è¿çæ¯ï¼è¹æå®æ¹å·²ç»æä¾äºä¸ä¸ªè¿æ ·åçæ¹æ³ï¼å°±æ¯NSFetchedResultsControllerã æ以ï¼å±ä»¬å æå¼ FBCDMasterViewController.hï¼æä¹åçfailedBankInfosï¼è¿ä¸ªNSArrayæ°ç»éªçµï¼å å ¥ä¸ä¸ªNSFetchedResultsController 代æ¿å®ï¼@interface FBCDMasterViewController : UITableViewController @property (nonatomic,strong) NSManagedObjectContext* managedObjectContext; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @endå¨FBCDMasterViewController.mçsynthesizeé¨åï¼å é¤ä»¥åçfailedBankInfos synthesize声æï¼å¹¶ä¸å å ¥ï¼@synthesize fetchedResultsController = _fetchedResultsController;å¦ä¸ä¸ª NSFetchedResultsController å¾é ·çç¹æ§æ¯ä½ å¯ä»¥å¨ViewDidUnloadä¸å°å®éæ°å£°æ为nilãè¿æå³çè¿ä¸ªæ¹æ³æä¸ä¸ªèªå¨çå å管çæºå¶ï¼ä¹å°±æ¯è¯´å½å 容ä¸å¨å±å¹ä¹ä¸åï¼å ¶å ç¨å åä¼èªå¨è¢«æ¸ 空ãè¦å®æè¿ä¸åï¼ä½ æéåçå°±æ¯å¨ViewDidUnloadä¸å°å®å£°æ为空ã - (void)viewDidUnload { self.fetchedResultsController = nil; }好äºï¼ç°å¨å°äºæ趣çé¨åäºï¼æ们å¼å§å建åå¾çæ°æ®çæ§å¶å¨ãé¦å æ们声æä¸ä¸ªå±æ§ï¼è®©å®éçç¨åºçè¿è¡æ£æµåå¾æ°æ®æ¯å¦åå¨ï¼å¦æä¸åå¨å°±åé ä¹ã å¨æ件ç头é¨å å ¥ä»¥ä¸ä»£ç ï¼ - (NSFetchedResultsController *)fetchedResultsController { if (_fetchedResultsController != nil) { return _fetchedResultsController; } NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"FailedBankInfo" inManagedObjectContext:managedObjectContext]; [fetchRequest setEntity:entity]; NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"details.closeDate" ascending:NO]; [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]]; [fetchRequest setFetchBatchSize:]; NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"]; self.fetchedResultsController = theFetchedResultsController; _fetchedResultsController.delegate = self; return _fetchedResultsController; }è¿æ®µä»£ç åæä»¬å¨ viewDidLoad æ¹æ³ä¸ä½¿ç¨çå¾æ¯ç¸ä¼¼ï¼å建ä¸ä¸ªfetch请æ±ï¼å°FailedBankInfoç©ä½å¼åºççï¼ä½æ¯ä»ç¶æä¸äºæ°çä¸è¥¿æ们è¦è®¨è®ºã é¦å ï¼å½æ们使ç¨NSFetchedResultsController,ï¼æä»¬å¿ é¡»è®¾ç½®ä¸ä¸ªæ°æ®åç±»å¨æ¥èµç»fetch请æ±ï¼æ°æ®åç±»å¨å°±æ¯æ们åè¯Core Dataæ们å¸æå¼å ¥çæ°æ®è¢«è¤¥åå¨åçæ¹æ³ã è¿ç§æ°æ®åç±»å¨åå¨çå¿ è¦å°±å¨äºå®ä¸ä» è½å¤ç¼æææè¿åçå±æ§åæ°æ®ï¼è¿å¯ä»¥ç¼æä¸ä¹ç¸å ³çææå±æ§åæ°æ®ï¼å°±å¥½åä¸ä¸ªå¤©æç人å¨åè¿äºäºæ ä¸æ ·ãå¦ææ们æ³è¦æ ¹æ®FailedBankDtailsä¸çclose dateè¿ä¸ªå±æ§ç¼ææ°æ®ï¼ä½å´æ³è¦æ¥æ¶FailedBankInfoä¸çæææ°æ®ï¼Core Dataéè¿è¿ä¸ªç¹æ§å°±å¯ä»¥å®æè¿æ ·çäºæ ã ä¸ä¸ä¸ªå£°æååçéè¦ï¼å°±æ¯è®¾ç½®åå¾çæ°æ®çç¼å²å¼çæ大å¼ï¼è¿æ£æ¯æ们å¨è¿ä¸ªåºæ¯ä¸æ³è¦ä½¿ç¨è¿ç§ç¹æ§çåå ï¼è¿æ ·çè¯ï¼fetchedæ¹æ³å°±ä¼èªå¨åå¾è®¾ç½®çå¼çæ°æ®é¡¹ç®ï¼ä¹åæå½æ们åä¸æ¥ççæ¶åç¨åºå°±ä¼èªå¨åå¾åç§æ°æ®ã å½æ们设置好è¿ä¸ªfetchçç¼å²å¼çæ¶åï¼æ们就å®æäºå建 NSFetchedRequestController 并ä¸å°å®ä¼ éç»äºfetch请æ±ï¼ä½æ¯è¿ä¸ªæ¹æ³å ¶å®è¿æ以ä¸å 个åæ°ï¼å¯¹äºmanaged object å 容ï¼æ们å¼ä¼ éå 容ãsection name key pathå 许æ们æç §éç§å±æ§æ¥åç»æåæ°æ®å 容ãæ件åçç¼ååååºè¯¥è¢«ç¨æ¥å¤çä»»ä½éå¤çä»»å¡ï¼æ¯å¦è¯´è®¾ç½®åç»æè æåæ°æ®çã ç°å¨æ们已ç»å®å ¨å建好äºä¸ä¸ªåå¾é¨åæ°æ®çæ¹æ³ï¼æ们ä¸é¢ä¿®æ¹ä¸ä¸ä»¥å使ç¨æ°æ®å å ¥æ°æ®çæ¹æ³ï¼è®©å®ä½¿ç¨æ们åå¾çæ°æ®ã - (void)viewDidLoad { [super viewDidLoad]; NSError *error; if (![[self fetchedResultsController] performFetch:&error]) { // Update to handle the error appropriately. NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); // Fail } self.title = @"Failed Banks"; } æ们å¨è¿éæ¥æä½æ们ç fetchedResultsController 并ä¸æ§è¡performFetch æ¹æ³æ¥åå¾ç¼å²ç第ä¸æ¹æ°æ®ã ä¹åï¼æ´æ°numberOfRowsInSectionæ¹æ³ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section]; return [sectionInfo numberOfObjects]; }æ´æ°cellForRowAtIndexPath æ¹æ³ï¼ - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = info.name; cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", info.city, info.state]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Set up the cell... [self configureCell:cell atIndexPath:indexPath]; return cell; }ç°å¨æ们å°ä¹åçé»è¾å为ä¸äºåå¼ç configureCell æ¹æ³ï¼æä»¬å¾ ä¼ä¼ç¨å°ãè¿ææåä¸ä»¶äºæ ï¼æ们éè¦ä¸º NSFetchedResultsController设置ä¸ä¸ªä»£çæ¹æ³ï¼å¥½æ¶æ¯æ¯é½æ模çï¼å ¶å®æ¯æä»appleå®æ¹çä¸ä¸ªä¾åç¨åºä¸copyè¿æ¥çï¼å°ä»¥ä¸æ¹æ³å å ¥å¨æ件çåºé¨ï¼ - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { // The fetch controller is about to start sending change notifications, so prepare the table view for updates. [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { // The fetch controller has sent all current change notifications, so tell the table view to process all updates. [self.tableView endUpdates]; }ç°å¨ç¼è¯è¿è¡ä½ çåºç¨çè¯ï¼è¡¨é¢ä¸çèµ·æ¥åºè¯¥é½æ¯ä¸æ ·çï¼ä½æ¯å¦æä½ ççæ§å¶å°çè¯ï¼æ人çäºæ æ£å¨åçå¦ï¼ SELECT 0, t0.Z_PK FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK ORDER BY t1.ZCLOSEDATE DESC total fetch execution time: 0.s for rows. SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t1.ZCLOSEDATE DESC LIMIT total fetch execution time: 0.s for rows. SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t1.ZCLOSEDATE DESC LIMIT total fetch execution time: 0.s for rows. ä½ å¯ä»¥çå°ï¼å¨èåï¼ NSFetchedResultsController æ£å¨ä» FailedBankInfoä¸åº¸ä¹å设置ç顺åºåå¾å¤§éçIDï¼æ¯æ¬¡åªç¼å²ä¸å®æ°éç项ç®ï¼å°±åæ们é¢æçä¸æ ·ã å¦æç´æ¥ä½¿ç¨SQLiteæ°æ®åºçè¯ï¼å°±ä¼æå¾å¤å·¥ä½è¦åäºï¼æ们ä½ä¸ä½¿ç¨Core Dataèçæ¶é´å¢ãä¹åçäºä»ä¹ï¼ è¿æ¯æå¶ä½å®æçä¾åç¨åºæºç ï¼æ¬¢è¿ä¸è½½ã
Delphi 做游戏热键(HotKey)的一个问题!~
1. 热键需要调用Windows api来实现,源码 RegisterHotkey
The源码 RegisterHotKey function defines a hot key for the current thread.
BOOL RegisterHotKey(
HWND hWnd, // window to receive hot-key notification
int id, // identifier of hot key
UINT fsModifiers, // key-modifier flags
UINT vk // virtual-key code
);
Parameters
hWnd
Identifies the window that will receive WM_HOTKEY messages generated by the hot key. If this parameter is NULL, WM_HOTKEY messages are posted to the message queue of the calling thread and must be processed in the message loop.
id
Specifies the identifier of the hot key. No other hot key in the calling thread should have the same identifier. An application must specify a value in the range 0x through 0xBFFF. A shared dynamic-link library (DLL) must specify a value in the range 0xC through 0xFFFF (the range returned by the GlobalAddAtom function). To avoid conflicts with hot-key identifiers defined by other shared DLLs, a DLL should use the GlobalAddAtom function to obtain the hot-key identifier.
fsModifiers
Specifies keys that must be pressed in combination with the key specified by the nVirtKey parameter in order to generate the WM_HOTKEY message. The fsModifiers parameter can be a combination of the following values:
Value Meaning
MOD_ALT Either ALT key must be held down.
MOD_CONTROL Either CTRL key must be held down.
MOD_SHIFT Either SHIFT key must be held down.
vk
Specifies the virtual-key code of the hot key.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Remarks
When a key is pressed, the system looks for a match against all thread hot keys. Upon finding a match, the system posts the WM_HOTKEY message to the message queue of the thread that registered the hot key. This message is posted to the beginning of the queue so it is removed by the next iteration of the message loop.
This function cannot associate a hot key with a window created by another thread.
RegisterHotKey fails if the keystrokes specified for the hot key have already been registered by another hot key.
If the window identified by the hWnd parameter already registered a hot key with the same identifier as that specified by the id parameter, the new values for the fsModifiers and vk parameters replace the previously specified values for these parameters.
See Also
GlobalAddAtom, UnregisterHotKey, WM_HOTKEY
2. 模拟键盘按键用API函数: keybd_event
The keybd_event function synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN message. The keyboard driver's interrupt handler calls the keybd_event function.
VOID keybd_event(
BYTE bVk, // virtual-key code
BYTE bScan, // hardware scan code
DWORD dwFlags, // flags specifying various function options
DWORD dwExtraInfo // additional data associated with keystroke
);
Parameters
bVk
Specifies a virtual-key code. The code must be a value in the range 1 to .
bScan
Specifies a hardware scan code for the key.
dwFlags
A set of flag bits that specify various aspects of function operation. An application can use any combination of the following predefined constant values to set the flags:
Value Meaning
KEYEVENTF_EXTENDEDKEY If specified, the scan code was preceded by a prefix byte having the value 0xE0 ().
KEYEVENTF_KEYUP If specified, the key is being released. If not specified, the key is being depressed.
dwExtraInfo
Specifies an additional -bit value associated with the key stroke.
Return Values
This function has no return value.
Remarks
Although keybd_event passes an OEM-dependent hardware scan code to Windows, applications should not use the scan code. Windows converts scan codes to virtual-key codes internally and clears the up/down bit in the scan code before passing it to applications.
An application can simulate a press of the PRINTSCREEN key in order to obtain a screen snapshot and save it to the Windows clipboard. To do this, call keybd_event with the bVk parameter set to VK_SNAPSHOT, and the bScan parameter set to 0 for a snapshot of the full screen or set bScan to 1 for a snapshot of the active window.
See Also
GetAsyncKeyState, GetKeyState, MapVirtualKey, SetKeyboardState
RTL级综合是什么意思?
RTL(Register Transfer Level,寄存器传输级)指:不关注寄存器和组合逻辑的源码细节(如使用了多少逻辑门,逻辑门之间的源码des_crypt源码连接拓扑结构等),通过描述寄存器到寄存器之间的源码逻辑功能描述电路的HDL层次。RTL级是源码比门级更高的抽象层次,使用RTL级语言描述硬件电路一般比门级描述简单高效得多。源码RTL级语言的源码最重要的特性是:RTL级描述是可综合的描述层次。
综合(Synthesize)是源码指将HDL语言、原理图等设计输入翻译成由与、源码或、源码非门等基本逻辑单元组成的源码门级连接(网表),并根据设计目标与要求(约束条件)优化所生成的源码逻辑连接,输出门级网表文件。RTL级综合指将RTL级源码翻译并优化为门级网表。解码 源码输出
xcode调试心得
没有系统的学习和总结过Xcode调试相关的知识,这里参考/里面的教程,总结一下调试相关的知识,算半拉翻译,半拉总结吧崩溃的表现一般来说:
SIGABRT(好处理)
EXC_BAD_ACCESS(一般内存问题)
SIGBUS
SIGSEGV
左面工具栏会按照线程分出bug所在,thread1一般主线程,其他线程的问题会在自己的位置显示。点开里面的方法都是看不懂的汇编(其实以前学过)。
对于Xcode下方有提示的bug一般很好解决,但是有时候只是简单的EXC_BAD_ACCESS,无从下手,左面工具栏中的方法也看不出所以然,这时要把顶部工具栏的breakpoint打开,也许左面就会显示出更多出问题的方法,如图打开brekpoints以后多出了所选的方法提示,找到了是数组的问题。当然也可以在左面下方滑块调节来显示出更多提示的搬家源码网站方法。
总结的来说,就是在左面栏里找到出问题的地方
App启动的流程
上面的图的调用关系说明了App是怎么启动的,除了main方法,其他方法都是看不到的,默认封装到系统的framework里,没法看源码
方法引用错误一般来说:
[UINavigationController setList:]: unrecognized selector sent to instance 0x6d4ed
1 [UINavigationController setList:]: unrecognized selector sent to instance 0x6d4ed这种要不就是这个类没这个方法,或者调用方法的对象错误,或者拼错,比较简单
看打印信息
没有打印信息的时候,可以点这个,有时候需要多点几次,可以有更详细的错误打印信息,lldb调试输入c功能是一样的,都是让程序继续运行
This class is not key value coding-compliant
Problems[:f] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is notkey value coding-compliant for the key button.'
12
3
4
5 Problems[:f] *** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]: this class is not
key value coding-compliant for the key button.'
1.有时会碰到这种错误,印象里是请求的网络列表返回为空,出现了个这么诡异的大型程序源码现象,这是一种情况。
2.NSUnknownKeyException指示了未知的key,而这个未知的key出现在MainViewController里,这个key的名字是button
先看nib,在这个例子里有一个button,和MainViewController的属性button连接了IBOutlet,但是@property对应的@synthesize没有写,出现了这个问题,虽然在iOS6可以不用写@synthesize了,但是在老版本可能还会出现这个问题
3.总结一下,“This class is not key value coding-compliant”这个问题出现在NIB相关的地方,一般是iboutlet已经连接,但是这个属性却不存在,常常发生在ib连着呢,属性给删了。
使用DEBUGGER
[self performSegueWithIdentifier:@"ModalSegue" sender:sender];
1 [self performSegueWithIdentifier:@"ModalSegue" sender:sender];这句话出问题了,ccl源码指标不知道怎么处理,可以在左面选中抛出的异常expection_throw
用LLDB的debugger po $eax会调用description方法,打印全部信息
po是point object,$eax是当前cpu注册者之一,如果选中了异常错误输入这个命令,这个注册者就是NSException对象,注意$eax是用于模拟器的,真机要用$r0
可以看大原因了,是segue问题,storyboard中的问题这里就定位了
类似的,还有这些debugger方法
po [$eax class] //可以看到 (id) $2 = 0xe NSException,数字不重要NSException是问题的名字po [$eax name]//得到exception的名字po[$eax reason]//得到错误原因(unsigned int) $4 = Receiver () has no segue with identifier 'ModalSegue'
12
3
4
5 po [$eax class] //可以看到 (id) $2 = 0xe NSException,数字不重要NSException是问题的名字
po [$eax name]//得到exception的名字
po[$eax reason]//得到错误原因(unsigned int) $4 = Receiver () has no segue with identifier 'ModalSegue'
NSAssert用法
- (void)doSomethingWithAString:(NSString *)theString{ NSAssert(theString != nil, @"String cannot be nil");NSAssert([theString length] = 3, @"String is too short");. . .}
12
3
4
5
6
7
8
9
- (void)doSomethingWithAString:(NSString *)theString
{
NSAssert(theString != nil, @"String cannot be nil");
NSAssert([theString length] = 3, @"String is too short");
. . .
}
NSAssert最为一种防御型的代码,目的就是一有错,程序就伴随着异常崩溃,或者说停止运行,不往下进行。上面的代码当传入空数组的时候就会打印这个:
-- ::. Problems[:c] *** Assertion failure in -[MainViewController doSomethingWithAString:], /Users/lipengxuan/Downloads/Problems/Problems/MainViewController.m:
1 -- ::. Problems[:c] *** Assertion failure in -[MainViewController doSomethingWithAString:], /Users/lipengxuan/Downloads/Problems/Problems/MainViewController.m:有的时候程序崩溃打印信息就会出现Assertion,那么就是这句话起作用了,这个时候可以继续运行(lldb c),可以看到更详细的打印信息。
总结一下,遇到Assertion failure,就可以下一步运行看更详细的信息
BreakPoint使用breakpoint 分Exception breakPoint和breakPoint
Exception breakPoint:程序崩溃异常了会立刻暂停到断点,点加号第一个就是添加Exception断点
breakPoint:随意放
Finally!终于到了传说中的打断点,这个很基本很经典的调试方法,事实上,断点和NSLog用法差不多,只不过不用你去写了
一个简单的例子,现在有个这么个方法
- (id)initWithStyle:(UITableViewStyle)style{ NSLog(@"init with style");if (self == [super initWithStyle:style]){ list = [NSMutableArray arrayWithCapacity:];[list addObject:@"One"];[list addObject:@"Two"];[list addObject:@"Three"];[list addObject:@"Four"];[list addObject:@"Five"];}return self;}
12
3
4
5
6
7
8
9
- (id)initWithStyle:(UITableViewStyle)style
{
NSLog(@"init with style");
if (self == [super initWithStyle:style])
{
list = [NSMutableArray arrayWithCapacity:];
[list addObject:@"One"];
[list addObject:@"Two"];
[list addObject:@"Three"];
[list addObject:@"Four"];
[list addObject:@"Five"];
}
return self;
}
程序运行后我发现貌似这个方法没有执行,这是一种猜测,通常我会在方法里加入打印信息,也可以打断点,在方法定义的地方加断点,如果调用这个方法了,就会停止在这里,起到了一样的作用。
接着是单步调试
打断点,然后点击跳跃的箭头,就可以一步步的执行了,更精细的步骤可以F6,期间可以随时打印想看的变量,比如在tableview的cellForRowAtIndexPath函数中用po indexPath打印出indexPath的值
(NSIndexPath *) $3 = 0x 2 indexes [0, 1]
1 (NSIndexPath *) $3 = 0x 2 indexes [0, 1]意思就是section 0 row 1
这样进行一步打印一次,可以看出indexes也在变化,知道出问题的敌方
Problems[:f] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'*** First throw call stack:(NSIndexPath *) $ = 0xa8a6c0 2 indexes [0, 5]
12
3
4
5
6
7 Problems[:f] *** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
*** First throw call stack:
(NSIndexPath *) $ = 0xa8a6c0 2 indexes [0, 5]
单步调试找出来了index row=5超出了数组的范围
总结一下,po命令非常实用,只要找到正确的调用方法,就可以打印对象信息
ZOMBIES问题EXC_BAD_ACCESS问题一般是内存问题,比如下面这种情况
程序停在了这里,EXC_BAD_ACCESS问题,但是也不知道具体问题是什么,这时候可以用zombie 工具检测
Xcode点击左上角项目名字-Edit Scheme-Diagnostics-Enable Zombie Objects,OK再次运行
会多出这段话
-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xcbe
1 -- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xcbe这段话什么意思呢?
创建一个对象,alloc一个对象会分配给这个对象一块内存,党这个对象release了,引用计数归零了,这块内存就会dealloc掉,之后其他对象就可以用这块内存。
但是可能有这样一种情况,正在有对象使用的内存被另一个对象企图指向使用,或者已经被释放的内存企图再次被释放。
僵尸工具的作用是让对象被released的时候,内存不dealloc,这块内存被标记为“undead”,如果其他对象想再用这块内存,app可以识别出错误并显示“message sent to deallocated instance”,就像上面的那段话一样。结合上面的代码
cell.textLabel.text = [list objectAtIndex:indexPath.row];
1 cell.textLabel.text = [list objectAtIndex:indexPath.row];这段话就是zombie对象出现的地方,一般来说textLabel 还有indexPath.row应该没问题,问题应该出现在list这个数组,而且zombie tool也说了是[__NSArrayM objectAtIndex:]的问题,利用NSLog打印list,可以看到内存地址和错误的地址相同,那么错误就定义在list了
-- ::. Problems[:c] list is 0xb0ed-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xb0ed0
12
3 -- ::. Problems[:c] list is 0xb0ed0
-- ::. Problems[:c] *** -[__NSArrayM objectAtIndex:]: message sent to deallocated instance 0xb0ed0
当然用dubugger 命令 p list也可以打印出这个结果
这就是zombie 工具起的作用,解决内存问题
总结的来说,如果出现了EXC_BAD_ACCESS错误,打开僵尸工具,再试一遍。但是没问题的时候不要打开这个工具,因为这个工具从不dealloc内存,只是标记内存为”undead”,会导致内存泄露,所以用的时候再打开,诊断后再关掉。
习惯问题应该看bug一样的视线看warning,能修复就修复。
- (void)buttonTapped:(id)sender{ NSLog(@"You tapped on: %s", sender);}
12
3
4
5
6
7 - (void)buttonTapped:(id)sender
{
NSLog(@"You tapped on: %s", sender);
}
比如这个按钮点击事件,%s是打印C-String,就是末尾是NUL的字符串,但是这里sender是按钮对象,所以会崩溃,不能忽略警告
å¦ä½å¼åä¸ä¸ª AVPlayer iPad ç¨åº
AirPlay 并ä¸æ¯æ°ä¸è¥¿ï¼å®å¨ iOS 4.3 SDK ä¸å°±åå¨äºãä½AirPlay API ä¸ä¸æææ°çä¸è¥¿å å ¥ãå ¶ä¸ä¸ä¸ªæ趣æ°ç¹æ§æ¯ iPad ç¨åºè½éè¿ Apple TV 2 ææ¾æ¥èª iOS 设å¤ä¸çå 容ï¼æè éåiPad 2 ä¸çå±å¹å 容ã
ç®èè¨ä¹ï¼AirPlay å°±æ¯å°åªä½å 容ææ¾å°é«æ¸ æ¾ç¤ºå¨ï¼çµè§ï¼æé«æ¸ é³æç³»ç»ä¸ãApple TV 2 åæ¯ç¨äºè¿æ¥ iOS 设å¤æ iTunesä¸é«æ¸ 设å¤ä¹é´çæ¡¥æ¢ã
éç iOS 5 çåºç°ï¼ä»»ä½ä½¿ç¨ AV Foundation ç±»çç¨åºé½è½ä»ç¨åºä¸ææ¾é³è§é¢å å®¹å° Apple TVãä½ å¯ä»¥ç¨MPMoviewPlayerController éè¿ AirPlay å°å½åæ£å¨ææ¾çå 容æå½±å°é«æ¸ çµè§æå ¶å®é«æ¸ æ¾ç¤ºè®¾å¤ä¸ãå¦ä¸ä¸ªè¿æ¥æ¯ä»UIWebView ææ¾è§é¢ï¼è¿æ¯æ¿å¨äººå¿çæ¹è¿ï¼å 为è¿æå³çæ们å¯ä»¥ç´æ¥ä» web ä¸å°å¨çº¿çé³è§é¢æå½±å°çµè§æè Apple TV 2 ä¸ã
ä½¿ç¨ AVFoundation æå½±è§é¢å 容
è¦å¨åºç¨ç¨åºä¸ä½¿ç¨ AVFoundationï¼éè¦å®ç° AVPlayer 并设置 allowsAirPlayVideo 为 YES 以å¼å¯ AirPlayï¼æè 设置为NO ä»¥å ³é AirPlayï¼å¦ä»¥ä¸ä»£ç æ示ï¼
-(BOOL)setAirPlay:(BOOL)airplayMode{
return self.player.allowsAirPlayVideo=airplayMode;
}
ç¼ååºç¨ç¨åº
为äºæ¼ç¤ºå¦ä½å建 AVPlayer åºç¨ç¨åºä»¥åå®ç° AirPlayï¼æ们å°å建ä¸ä¸ª Single View Application(注æå¾é UseStoryboards)ï¼æ°å»ºä¸ä¸ª AVPlayer 类并å®ç° AirPlay ç¹æ§ã
å建ä¸ä¸ª Single View Applicationãç®æ ç±»åå¯ä»¥éæ© iPhoneãiPod æè iPadãå建项ç®ä¹åï¼åå¯¼å ¥ AV Foundation æ¡æ¶ã
æ¥çå建ä¸ä¸ªæ° classï¼å½å为 Playerï¼å¹¶ç»§æ¿ UIView ç±»ãå¨å¤´æ件ä¸ï¼å å ¥ AVPlayer 类并å¢å ä¸ä¸ª AVPlayer å±æ§ãå¦ä¸å代ç æ示ã
@class AVPlayer;
@interface Player : UIView
@property(nonatomic, strong) AVPlayer * player;
æ¥ä¸æ¥å°±æ¯å®ç° Player ç±»ã
æ们å®ç°äºæåºæ¬ç AVPlayer éè¦å®ç°çæèµ·ç ç 4 个æ¹æ³ï¼åæ¶è¿æä¾äºä¸ä¸ªæ¹æ³ä½ä¸ºæ们ç AirPlay å¼å ³ã
é¦å æ们éè¦ä¸ä¸ª AVLayer ç±»çå è£ ç±»ã该类æ¯ä¸ä¸ª CALayer åç±»ï¼ç¨äºå¯¹åªä½çå¯è§å 容è¿è¡ç®¡çãå建å è£ ç±»ç代ç å¦ä¸ï¼
+ (Class)layerClass {
return [AVPlayerLayer class];
}
ç¶å为éè¦ä¸ä¸ªæ¹æ³ï¼å®ä¾åä¸ä¸ª AVPlayer 对象ï¼æ们å¨å¤´æ件ä¸å®ä¹çï¼ãå¦ä¸å代ç æ示ã
-(AVPlayer *) player{
return [(AVPlayerLayer *)[self layer] player];
}
å¨ setPlayer æ¹æ³ä¸ï¼å¦ä¸å代ç æ示ï¼æä¸ä¸ª AVPlayer åæ°ï¼ç¨äºå°ä¸ä¸ª AVPlayer å®ä¾æ·»å å° UIViewãè¿ä¸ª UIView åç±»ï¼å°ç¨å¨ä¸» View Controller ä¸ã
- (void)setPlayer:(AVPlayer*)player {
[(AVPlayerLayer*)[self layer] setPlayer:player];
}
è¿ä¸ªç±»çæåä¸ä¸ªæ¹æ³æ¯ setAirPlay æ¹æ³ãå ¶åæ°å°ç¨äºæå® AVPlayer UIView (Player)çallowsAirPlayVideo å±æ§ãå¦ä»¥ä¸ä»£ç æ示ã
-(BOOL)setAirPlay:(BOOL)airplayMode{
return self.player.allowsAirPlayVideo=airplayMode;
}
为è§é¢çè¾åºæå®å¾å±(AVPLayerLayerï¼æ¶ï¼å¯ä»¥æå®ä»»ææ°éçå¾å±,åªè¦è½æ¹ä¾¿æ§å¶å 容æ¾ç¤ºãæ¯å¦è¯´å¤çé³é¢åè§é¢ä¹é´çæ¶é´åæ¥ãéè¿setDisplayModeï¼ä½ å¯ä»¥è®¾ç½®ç¨äºæ¾ç¤ºçå¾å±ï¼å å建ä¸ä¸ª AVPlayerLayer 对象ä½ä¸ºæ¾ç¤ºå¾å±ï¼ç¶åä¿®æ¹å®çå±æ§ãé»è®¤æ¯AVLayerVideoGravityResizeAspect å±æ§ï¼å¦å¤ä¹å¯ä»¥è®¾ç½® AVLayerVideoGravityResizeAspectFill å±æ§åAVLayerVideoGravityResize å±æ§ã AVLayerVideoGravityResizeAspect ä¿æè§é¢ç宽é«æ¯å¹¶ä½¿ææ¾å 容èªå¨éåºææ¾çªå£ç大å°ãAVLayerVideoGravityResizeAspectFill ååè 类似ï¼ä½å®æ¯ä»¥ææ¾å 容填å èä¸æ¯éåºææ¾çªå£ç大å°ãæåä¸ä¸ªå¼ä¼æ伸ææ¾å 容以éåºææ¾çªå£ã
Player ç±»çå®æ´ä»£ç å¦ä¸ï¼
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@class AVPlayer;
@interface Player : UIView
@property(nonatomic, strong) AVPlayer * player;
-(BOOL) setAirPlay:(BOOL) airplayMode;
@end
#import "Player.h"
#import <AVFoundation/AVFoundation.h>
@implementation Player
+ (Class)layerClass {
return [AVPlayerLayer class];
}
-(AVPlayer *) player{
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer*)player {
[(AVPlayerLayer*)[self layer] setPlayer:player];
}
//Enable or disable AirPlay mode
-(BOOL)setAirPlay:(BOOL)airplayMode{
return self.player.allowsAirPlayVideo=airplayMode;
}
@end
å¨ç¨åºä¸ä½¿ç¨ Player ï¼UIViewåç±»ï¼
ä¸è¿°ä»£ç å æ¬äºPlayer ï¼AVPlayer ç UIView åç±»ï¼ç.hæ件å.m æ件ã
è¦å¨ä¸ä¸ª UIViewController ä¸ä½¿ç¨è¿ä¸ª AVPlayer è§å¾ï¼æå¼ Xcode çæ äºæ¿(storyboard,åææ¯å建项ç®æ¶ä½¿ç¨äºâuseStoryboardâé项ï¼ãéä¸UIView(ä¸æ¯ UIViewControllerï¼å¹¶å°å®ç类修æ¹ä¸º Playerãä½ å¯ä»¥å¨ Identity é¢æ¿ç customeclass å段è¾å ¥ï¼ä¹å¯ä»¥ä»å®çä¸æå表ä¸éæ©ã
æå¼ Assistant Editor ,为 UIView åç±» Player å建ä¸ä¸ª IBOutlet,ä» view ç¨å³é®æå°å³è¾¹ç头æ件ä¸å³å¯ãå¦ä¸å¾æ示ã
å建IBActions å IBOutlets è¿æ¥
å建ä¸ä¸ªå§æå° klViewController ãå¨ Player ä¸å³é®ï¼ä» IBOutlet æä¸ä¸ªè¿æ¥çº¿å° klViewController (dock ä¸çé»è²æ¹å) ï¼å¦ä¸å¾æ示ã
æ·»å å§æè¿æ¥ klViewController
å¨æ äºçä¸ï¼å ä¸ä¸ª Toolbar ãå¨ Toolbar æ¯å å ¥ä¸¤ä¸ªæé®ï¼åå«å°æ ç¾ææ¬è®¾ç½®ä¸º Play å Pauseãç¶åå ä¸ä¸ª Switchï¼ç¨äºåæ¢AirPlay å¼å ³ç¶æãä¸ºè¿ 3 个æ§ä»¶å建ç¸åºç IBActionï¼å¯ä»¥ç¨ Assistant Editorï¼ãå©ä¸çäºæ å¨ klViewController ç±»ä¸è¿è¡ã
æå¼ klViewController.h æ件ï¼å å ¥ @class Player å @class AVPlayer è¯å¥å¹¶å¯¼å ¥ AVFoundation æ¡æ¶å Player.hãæºä»£ç è§ä¸ï¼
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "Player.h"
@class Player;
@class AVPlayer;
@interface klViewController : UIViewController
声æä¸ä¸ª AVPlayer å±æ§ï¼ç¨äºå è½½å° Palyer è§å¾ä¸ã
声æä¸ä¸ª NSURL å±æ§ãç¶åæ¯ Switch æ§ä»¶ç IBOutletï¼ç¨ Assistant Editorå建ï¼ã
kvLiewController.h æ件代ç å¦ä¸ï¼
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import Player.h
@class Player;
@class AVPlayer;
@interface klViewController : UIViewController
@property(nonatomic, strong) AVPlayer * myPlayer;
@property(nonatomic, strong) NSURL * avContentUrl;
@property (strong, nonatomic) IBOutlet Player *airPlayView;
@property (nonatomic, retain) IBOutlet UISwitch * AirPlaySwitch;
- (IBAction)PlayVideo:(id)sender;
- (IBAction)PauseVideo:(id)sender;
- (IBAction)isAirPlayOn:(id)sender;
@end
æ¥ä¸æ¥æ¯ç±»çå®ç°æ件ãå¨ viewDidLoad æ¹æ³ä¸ï¼ç¨ä¸ä¸ªè§é¢æ件ç URL å°ååå§å avContentUrlï¼
avContentUrl = [[NSURL alloc] initWithString
ç¶åç¨ avContentUrl åå§åä¸ä¸ª AVPlayer ï¼ç¨äºç»å¤´æ件ä¸å®ä¹ç myPlayer å±æ§èµå¼ãå° myPlayer èµç» airPlayView ç player å±æ§ï¼airPlayerView æ¯ä¸ä¸ª Player 对象ãå¦ä¸å代ç æ示ï¼
self.myPlayer = [AVPlayer playerWithURL:avContentUrl];
[airPlayView setPlayer:[self myPlayer]];
ç°å¨ï¼è¦å®ç°ä¸¤ä¸ªæé®çè§é¢ææ¾åæååè½ï¼ä»¥å Switch æ§ä»¶ç AirPlay å¼å ³åè½ãææ¾æé®ç action æ¹æ³ä»£ç å¦ä¸æ示ï¼
- (IBAction)PlayVideo:(id)sender {
[self.myPlayer play];
}
æåæé®ç action æ¹æ³ä»£ç å¦ä¸æ示ï¼
- (IBAction)PauseVideo:(id)sender {
[self.myPlayer pause];
}
Switch æ§ä»¶ç action æ¹æ³ä»£ç å¦ä¸æ示ï¼
- (IBAction)isAirPlayOn:(id)sender {
AirPlaySwitch = (UISwitch *) sender;
if (AirPlaySwitch.on) {
[airPlayView setAirPlay:NO];
}else
{
[airPlayView setAirPlay:YES];
}
}
ç»å°¾
å¦ä¸ä¸ªä¸ AirPlay æå ³çå±æ§æ¯ usesAirPlayVideoWhileAirPlayScreenIsActive ï¼å®ç¨äºèªå¨å¨å¨ææ¾æé´å° AVPlayer åæ¢å° AirPlay,å½ç¶ä» ä» æ¯å¨ AirPlay å·²å¼å¯çæ åµä¸ãé»è®¤æ¯ false çã
è¦è¿è¡æ¬ç¤ºä¾ç¨åºï¼éè¦å¨ iPad ä¸è¿è¡ï¼å¹¶å° iPad è¿æ¥è³ Apple TV åä¸ wifi ç½ç»ï¼ç¶åè§é¢èµæºç URL å¿ é¡»æ¯ææçãå¨æ¨¡æå¨ä¸AirPlay æ¯æ æçã
klViewController.h æ件
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "Player.h"
@class Player;
@class AVPlayer;
@interface klViewController : UIViewController
@property(nonatomic, strong) AVPlayer * myPlayer;
@property(nonatomic, strong) NSURL * avContentUrl;
@property (strong, nonatomic) IBOutlet Player *airPlayView;
@property (nonatomic, retain) IBOutlet UISwitch * AirPlaySwitch;
- (IBAction)PlayVideo:(id)sender;
- (IBAction)PauseVideo:(id)sender;
- (IBAction)isAirPlayOn:(id)sender;
@end
klViewController.m æ件
#import "klViewController.h"
@implementation klViewController
@synthesize airPlayView;
@synthesize avContentUrl, myPlayer, AirPlaySwitch;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
//This is an Apple sample video
avContentUrl = [[NSURL alloc] initWithString:@" self.myPlayer = [AVPlayer playerWithURL:avContentUrl];
[airPlayView setPlayer:[self myPlayer]];
[self.myPlayer play];
}
- (void)viewDidUnload
{
[self setAirPlayView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)PlayVideo:(id)sender {
[self.myPlayer play];
}
- (IBAction)PauseVideo:(id)sender {
[self.myPlayer pause];
}
- (IBAction)isAirPlayOn:(id)sender {
AirPlaySwitch = (UISwitch *) sender;
if (AirPlaySwitch.on) {
[airPlayView setAirPlay:NO];
}else
{
[airPlayView setAirPlay:YES];
}
}
@end