- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
请让我详细说明我在做什么以及正在发生什么。我是 cocoa 和 Objective-C 的新手。请宽容一点。谢谢。
我在做什么
我正在创建一个文件搜索实用程序,用户将在其中输入关键字,cocoa 应用程序将搜索整个系统文件,如果关键字存在于文件路径中,则会将其显示给用户。
问题
当点击“取消”按钮时,用户界面会变得无响应。
编码部分从这里开始:
图书馆
/// this funciton will be called with
/// keyword : the word that should be present in the file path
/// selector : a function tha will be called when ever the function will find a matching file
/// selector: object of the class that has the funciton (selector)
-(NSMutableArray *)searchWholeSystem:(NSString *)keyword
selector:(SEL)aselector
target:(id)atarget
{
/// _cancelSearch is a property which can be set by the user of the class
/// when the scann will be started it will be set to as default : NO
_cancelSearch = NO;
/// list of all the files that are matched with the keyword
NSMutableArray* fileList = [[NSMutableArray alloc]init];
///autoreleasepool to release the unused memory
@autoreleasepool {
///getMainDirectories is local function that will get the user direcotires or the target direcoties where the search will be done
NSMutableArray* directories = [self getMainDirectories];
///one by one search all the direcoties for the matching files
for (NSString* dir in directories) {
NSMutableArray* resultList = [self getAllFiles:dir tag:keyword target:atarget selector:aselector];
for (int i = 0; i<[resultList count]; i++) {
///if cancel then return the as yet result array
if (_cancelSearch)
return fileList;
NSString* path = [resultList objectAtIndex:i];
GenericResultModel* sfile = [[GenericResultModel alloc]init];
sfile.Column1 = [path lastPathComponent];
sfile.Column2 = path;
[fileList addObject:sfile];
}
}
return fileList;
}
}
///getAllFiles will be having
///sPath : source path where the search will be performed
///tag: tag is the keyword that need to found in the path
/// selector : a function tha will be called when ever the function will find a matching file
/// selector: object of the class that has the funciton (selector)
-(NSMutableArray*)getAllFiles:(NSString*)sPath tag:(NSString*)_tag target:(id)atarget selector:(SEL)aselector
{
// fileList is the result that will contain all the file names that has the _tag
NSMutableArray* fileList = [[NSMutableArray alloc]init];
@autoreleasepool {
// _tag is the keyword that should be present in the file name
_tag = [_tag lowercaseString];
///getting all contents of the source path
NSArray *contentOfDirectory=[[NSFileManager defaultManager] contentsOfDirectoryAtPath:sPath error:NULL];
for(int i=0; i < [contentOfDirectory count]; i++)
{
if (_cancelSearch)
return fileList;
NSString *filePathInDirectory = [contentOfDirectory objectAtIndex:i];
NSString* fullPath = [NSString stringWithFormat:@"%@/%@", sPath, filePathInDirectory];
if ([FMH isdirOrApp:fullPath])
{
if ([[fullPath lowercaseString] rangeOfString:_tag].location != NSNotFound && [fileList indexOfObject:fullPath] == NSNotFound)
{
[fileList addObject:fullPath];
[atarget performSelector:aselector withObject:fullPath];
}
if (_cancelSearch)
return fileList;
NSMutableArray* files = [self getAllFiles:fullPath tag:_tag target:atarget selector:aselector];
for (NSString* f in files)
{
if ([[f lowercaseString] rangeOfString:_tag].location != NSNotFound && [fileList indexOfObject:fullPath] == NSNotFound)
{
[fileList addObject:f];
[atarget performSelector:aselector withObject:f];
}
if (_cancelSearch)
return fileList;
}
}
else
{
NSString* fileN = [fullPath lastPathComponent];
if ([[fileN lowercaseString] rangeOfString:_tag].location != NSNotFound && [fileList indexOfObject:fullPath] == NSNotFound)
{
[fileList addObject:fullPath];
[atarget performSelector:aselector withObject:fullPath];
}
if (_cancelSearch)
return fileList;
}
}
}
return fileList;
}
从 UI 调用库函数
-(void)cancelClick:(id)sender
{
macSearch.cancelSearch = YES; ///setting the cancel to yes so that the library function may come to know that the search is canceled and now stop searching return what ever is searched
}
-(void)wholeSystemScan:(id)sender
{
if([[searchKeyword stringValue] length] < 1)
{
[[[MessageBoxHelper alloc] init] ShowMessageBox:@"Please enter a keyword to search." Title:@"Information" IsAlert:YES];
return;
}
[self ScanStartDisableControls];
NSString* keyw = [searchKeyword stringValue];
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.techheal.fileSearch", 0);
dispatch_async(backgroundQueue, ^{
[macSearch searchWholeSystem:keyw selector:@selector(refreshSelector:) target:self];
[self ScanCompletedEnableControls];
});
}
最佳答案
您真的应该使用 NSOperationQueue
来处理 NSOperation
的执行。当您自己对操作调用 -start
时,操作是在调用 -start 的线程(即主线程)上进行的。这可能不是您想要的,因为通过在主线程上完成所有工作,只有在操作完成后才能尝试更新 UI。要解决该问题,只需使用 NSOperationQueue
。
您在 init
方法中创建了 NSOperationQueue
(queue
)。然后你的 start:
方法看起来像这样:
-(void)start:(id)sender
{
[DataList removeAllObjects];
[tableView reloadData];
NSString* keyw = [searchTextBox stringValue];
searcher = [[MacFileSearchReVamp alloc] initWithFileName:keyw selector:@selector(refreshSelector:) target:self];
searcher.delegate = self;
[queue addOperation:searcher];
// [searcher startSearch];
}
你可以在这里看到,我们没有直接调用 startSearch
,而是简单地将 searcher
对象添加到 queue
中,它负责执行在后台线程上操作。
您的stop:
方法变为:
- (IBAction)stop:(id)sender
{
[queue cancelAllOperations];
// [searcher stopSearch];
}
然后,解决当您有大量搜索结果时卡住 UI 的性能问题。您当前代码中发生的事情是后台线程如此快速地找到结果并尝试调用主线程来更新主线程因工作重载而变得无响应的每个结果。为了缓解这种情况,您需要减少对主线程的调用来更新 UI。虽然有很多方法可以做到这一点,但一种方法是简单地让后台线程将其结果存储 0.5 秒,然后调用主线程并传递这些结果。然后它每 0.5 秒重复一次,直到完成。虽然不完美,但应该可以提高响应能力。
此外,虽然可能不需要进行以下更改,但对我来说它们似乎是一种更清晰的设计。当你想从在后台线程中运行的 NSOperation
对象进行通信时,要执行一些应该在主线程上完成的更新 UI 之类的操作,让操作对象本身担心确保在主线程上调用刷新选择器。因此,删除 dispatch_async
调用,并将刷新选择器更改为接受路径数组的方法:
-(void)refreshSelectorWithPaths:(NSArray *)resultPaths
{
for (NSString *resultPath in resultPaths) {
GenericResultModel* sfile = [[GenericResultModel alloc]init];
sfile.Column1 = [resultPath lastPathComponent];
sfile.Column2 = resultPath;
[DataList addObject:sfile];
}
[tableView reloadData];
}
您应该删除检查 DataList
是否已包含该条目的代码,因为随着结果数量的增加,这对性能来说将是灾难性的,并且考虑到更新的 NSOperation
代码,这将是不必要的。
#define MD_PROGRESS_UPDATE_TIME_INTERVAL 0.5
-(NSMutableArray*)getAllFiles:(NSString*)sPath tag:(NSString*)_tag target:(id)atarget selector:(SEL)aselector {
// fileList is the result that will contain all the file names that has the _tag
NSMutableArray* fileList = [[NSMutableArray alloc]init];
NSMutableArray *fullPaths = [NSMutableArray array];
@autoreleasepool {
// _tag is the keyword that should be present in the file name
_tag = [_tag lowercaseString];
/* subpathsOfDirectoryAtPath:error: gets all subpaths recursively
eliminating need for calling this method recursively, and eliminates
duplicate results */
NSArray *subpaths = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:sPath error:NULL];
NSUInteger subpathsCount = subpaths.count;
NSDate *progressDate = [NSDate date];
for (NSUInteger i = 0; i < subpathsCount; i++) {
if ([self isCancelled]) break;
NSString *subpath = [subpaths objectAtIndex:i];
if ([[[subpath lastPathComponent] lowercaseString] rangeOfString:_tag].location != NSNotFound) {
NSString *fullPath = [sPath stringByAppendingPathComponent:subpath];
[fileList addObject:fullPath];
[fullPaths addObject:fullPath];
if (ABS([progressDate timeIntervalSinceNow]) > MD_PROGRESS_UPDATE_TIME_INTERVAL) {
[atarget performSelectorOnMainThread:aselector withObject:fullPaths waitUntilDone:NO];
[fullPaths removeAllObjects];
progressDate = [NSDate date];
}
}
}
if (fullPaths.count) [atarget performSelectorOnMainThread:aselector withObject:fullPaths waitUntilDone:NO];
}
return fileList;
}
以上代码使用 fullPaths
来存储每 0.5 秒间隔的完整路径。结果找到,路径被添加到fullPaths
,然后我们检查距离上次我们告诉主线程刷新是否已经过了0.5秒。如果有,我们调用刷新选择器,然后从 fullPaths
数组中删除这些条目。
这是您的概念验证的改进版本(更新后性能增强):
关于objective-c - COCOA: 无法点击取消按钮,UI 挂起且无响应,如何处理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35174831/
情况我想使用 ui-date 在我的应用程序中设置/编辑日期。我使用最新稳定版本的 Angular、Angular-UI、JQuery-UI 等。 问题一旦使用日期选择器选择了日期,我的模型中的日期将
编辑: jQuery UI 可选择小部件内置了一个回调,stop,我需要知道如何以编程方式触发此事件。 (措辞不佳)我已将事件监听器附加到 jQuery UI Selectable Widget 。如
我正在尝试建立一个下一个JS与尾风用户界面提供的反应组件的网络应用程序。顺风用户界面是在幕后使用无头用户界面。。默认情况下,Next JS将构建服务器端组件,除非您在页面顶部添加“使用客户端”。不幸的
我正在尝试建立一个下一个JS与尾风用户界面提供的反应组件的网络应用程序。顺风用户界面是在幕后使用无头用户界面。。默认情况下,Next JS将构建服务器端组件,除非您在页面顶部添加“使用客户端”。不幸的
我正在尝试应用这个 SlickGrid 示例: http://mleibman.github.com/SlickGrid/examples/example4-model.html 到我自己的网络项目。
我想整理我的 Schemas为我的实体类生成,DTO 类位于 Springdoc ui . 我可以对 tags 进行排序和 operations通过以下配置 yml文件,但我的模式不是按排序顺序排列的
有谁知道阻止 ui-sref 重新加载状态的方法吗? 我无法通过“$stateChangeStart”事件执行此操作,因为 ui-sref 仅更改参数而不更改状态名称。 我的左边是书单,左边是书的详细
我正在 jquery ui 对话框中使用 jquery ui 自动完成小部件。当我输入搜索文本时,文本框缩进(ui-autocomplet-loading)但不显示任何建议。 var availabl
我正在尝试将 Kendo UI MVVM 框架与 Kendo UI 拖放机制结合使用;但我很难找到如何将数据从 draggable 对象中删除。 我的代码是这样的...... var viewMode
Kendo UI Web 和 Kendo UI Core 之间有什么区别 https://www.nuget.org/packages/KendoUIWeb http://www.nuget.org/
我正在尝试将 Kendo UI MVVM 框架与 Kendo UI 拖放机制结合使用;但是我很难找到如何从 draggable 对象中删除数据。 我的代码是这样的…… var viewModel =
使用 Angular JS - UI 路由器,我需要从我的父 View project.details 到我的 subview project.details.tasks 进行通信。我的 subvie
KendoUI 版本 2013.3.1119使用 Kendo MVVM 我有一个我构建的颜色选择器,它使用平面颜色选择器和使用调色板的颜色选择器。它们都可以正常运行,但平面颜色选择器的布局已关闭, s
我在非 UI 线程上,我需要创建并显示一个 SaveDialog。但是当我尝试显示它时:.ShowDialog() 我得到: "An unhandled exception of type 'Syst
我正在试验 jquery-ui 并查看和克隆一些示例。在一个示例(自动完成的组合框)中,我看到一个带有 ui-widget 类的 anchor (a) 元素,它与包含的 css 文件中的 .ui-wi
我需要返回一个 UI 列表,我用这个方法: getList(): Observable { return this.httpClient.get("/api/listui").pipe
我有 ui-grids在 angular-ui-tabs ,它们位于 ng-if 中以避免呈现问题。如果有更多数据并且网格进入滚动模式,则单击选项卡时数据会完全消失。我相信这是一个 ui-grids-
这似乎是一个通用的问题,与其他几个 React 开源框架相比,我真的很喜欢 Material ui 的可扩展性。 问题 “@material-ui/core”和“@material-ui/lab”中的
我有一个根页面(index.html),带有侧边栏(“菜单”)和主要内容 div(“主”),因此有两个 ui-view div - 一个称为“菜单”,一个称为“主”。 当主要内容区域有网站列表 (/s
有人在http://jsfiddle.net/hKYWr/上整理了一个很好的 fiddle 。关于使用 angular-ui 和 jqueryui sortable 来获得良好的可排序效果。 如何在两
我是一名优秀的程序员,十分优秀!