皮皮网

【源码阅读神器】【王者荣耀源码图片】【奇缘幻境的源码】synthesize 源码

来源:抽奖小程序模板源码 时间:2024-12-24 08:27:09

1.如何使用NSFetchedResultsController
2.Delphi 做游戏热键(HotKey)的源码一个问题!~
3.RTL级综合是源码什么意思?
4.xcode调试心得
5.如何开发一个 AVPlayer iPad 程序

synthesize 源码

如何使用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,寄存器传输级)指:不关注寄存器和组合逻辑的源码细节(如使用了多少逻辑门,逻辑门之间的源码源码阅读神器连接拓扑结构等),通过描述寄存器到寄存器之间的源码逻辑功能描述电路的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.'

1

        2

        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];

       这句话出问题了,禅意花园原创源码不知道怎么处理,可以在左面选中抛出的异常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'

1

        2

        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");. . .}

1

        2

        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;}

1

        2

        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]

1

        2

        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

1

        2

        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);}

1

        2

        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