http://www.raywenderlich.com/1948/itunes-tutorial-for-ios-how-to-integrate-itunes-file-sharing-with-your-ios-app

The iPad and iOS 4 have a great new feature called File Sharing that provides a convenient way for users to transfer files between their computer and your app.

But figuring out exactly what you need to do to get this working in real-world scenarios can be tricky. So I thought it would be useful to write an iTunes tutorial that covers how to do that with a real app, step by step!

If you’ve been following the development of the “Scary Bugs” app in the simple app tutorial or NSCoding tutorial, it’s all been leading up to this!

In those tutorials, we’ve been creating an app that allows you to collect scary bugs and rate them. However, it would be even better if users could share bugs with their friends (or mortal enemies!)

So let’s do it! You’ll need a copy of the ScaryBugs project where we last left off – if you don’t have it already, you can grab a copy here.

File Sharing Overview

Let’s start with an overview with how File Sharing works.

To enable File Sharing in your app, you simply set the boolean flag “UIFileSharingEnabled” in your info.plist.

iTunes will then display anything you save to the Documents directory
in your app to the user, when they go to the “Apps” page in iTunes and
scroll to the bottom:

Of course, for this to be of any use, your app needs to have some smarts in it.

First, your app needs to be able to detect the files that the user puts into this directory.

Second, your app needs to be able to deal with the fact that the
contents of this folder can change at any time. The user can rename
files, delete files, or even put garbage in there if they want.

Third, your app needs to have a way to export your app’s document as a single file, even if it consists of multiple files.

“But wait!”, you may say. “What about packages, can’t I just set up
my app to use a Document Package and have my folder of files be treated
like a single file?”

Well, about that…

File Sharing and Document Packages

If you want to skip this section and move on, the TLDR summary is:
Document Packages don’t work the way you’d expect with iTunes currently,
so you need to use an alternative solution.

But if you’re curious about them and why they don’t work, here’s some more information.

For those of you who are unfamiliar, packages are a way that iOS and
MacOS can treat a directory of files as a single file. This is used for
.apps – these are actually directories of files, with a standard
structure (a “bundle”).

Many apps on the Mac use store their documents as packages – Document Packages
to be specific. Again, these are simply directories with whatever
makes up the app’s document contents inside, often with a particular
file extension.

You can register your app as the owner of files with a particular
extension in your info.plist, and mark the type as a package with the
LSTypeIsPackage key. If you do this on the Mac and install your app to
your Applications folder, the OS will detect it and start treating any
directory with your registered extension as a package.

You can register your iOS app as being the owner of a file
type/extension that is a package as well. If you save it to the
Documents directory, you might think that it will show up in File
Sharing as a single file rather than a directory.

Well it does – but it doesn’t work exactly the way you’d think. It will show up in iTunes as a single file just fine:

However, when you save the package to disk, it will be saved as a folder, not as a single file:

To make it worse, there’s no way for the user to import the folder
back into iTunes, and they can mess around with the contents of the
folder.

You could get the package to show up as a single file if you also had
a companion app installed on the Mac, but most of the time as app
developers we won’t be able to guarantee that the user has the companion
app installed as well. Plus, this wouldn’t work at all on a PC.

So as I said, document packages just aren’t the way to go with File
Sharing at this point. “But wait!”, you may say. “What about Pages and
other Apple iPad apps, don’t they use packages just fine?”

Well, about that…

File Sharing and Pages

If you’re curious how Apple handles things, if you don’t have Pages you might want to check out this great article with a summary of the File Sharing process with Pages.

But as a quick summary, here’s how I understand Pages to work:

  • The Pages documents aren’t actually packages, they are zipped packages so they can be treated as a single file.
  • Pages actually has two separate lists of documents:
    • The list of documents that Pages uses, in a private directory not available to the user.
    • The list of documents that is available for File sharing, in the Documents directory.
  • The user has to actually take a step to import or export a document
    to/from File Sharing (it doesn’t just automatically show up). I believe
    this is because it would be a performance penalty to constantly create
    zipped copies of documents mirroring the actual documents.

So, in our case with Scary Bugs, we are in a similar situation since
we have documents made up with several files. Therefore, we are going
to take Apple’s approach, and have manual steps to import/export files
to File Sharing, and export our documents as zipped directories.

Importing and Exporting Our Documents

Ok enough talk, time for action! First things first – we’re going to use some helper code from the users at CocoaDev.com for gzip/gunzip. So grab a copy, and drag the two files into the Helpers group of your project.

Also, while you’re at it, these files require zlib, so go to your project settings and add “-lz” to your “Other Linker Flags”.

Now onto the code! Let’s start by adding the code to our ScaryBugDoc
to support exporting and importing our documents as a zipped file that
we can share with File Sharing.

First make the following mods to ScaryBugDoc.h:

// After @interface
- (NSString *)getExportFileName;
- (NSData *)exportToNSData;
- (BOOL)exportToDiskWithForce:(BOOL)force;
- (BOOL)importFromPath:(NSString *)importPath;

Just declaring some methods we’re about to implement here. Switch over to ScaryBugDoc.m and let’s implement them one by one:

1) Implement getExportFileName

// Add to top of file
#import "NSData+CocoaDevUsersAdditions.h"
 
// Add new method
- (NSString *)getExportFileName {
NSString *fileName = _data.title;
NSString *zippedName = [fileName stringByAppendingString:@".sbz"];
return zippedName;
}

This method will return the name that we’re going to export our bug as. We don’t want to export the bug with a simple numeric directory name like our documents are named internally, because then our user will have no good way of knowing what’s inside. So instead, we use the title of the bug to construct the filename.

Speaking of which, I’m not sure if there’s an easy way to massage the filename so it doesn’t contain any unsupported characters on both Mac and Windows, anybody know a solution to that?

The other thing to note is that we end the filename with an extension “.sbz” which we’ll register our app as being able to open later. When I was first playing around with this, I tried using a double extension such as “.scarybug.gz”, but when I was trying to open the attachment from Mail, it would never launch my app, and I suspect it didn’t like the double extension. So I recommend using just a single extension for now.

2) Implement exportToNSData

So now we need a method to take our directory and convert it into a single buffer of NSData so we can write it out to a single file.

There are different ways to do this – one popular way is to zip the directory up using the open source ZipArchive library. Another popular way is to use a combination of tar and gzip code. But I thought I’d show you another way: using NSFileWrapper to serialize the data, then gzipping that up.

- (NSData *)exportToNSData {
NSError *error;
NSURL *url = [NSURL fileURLWithPath:_docPath];
NSFileWrapper *dirWrapper = [[[NSFileWrapper alloc] initWithURL:url options: error:&error] autorelease];
if (dirWrapper == nil) {
NSLog(@"Error creating directory wrapper: %@", error.localizedDescription);
return nil;
}
 
NSData *dirData = [dirWrapper serializedRepresentation];
NSData *gzData = [dirData gzipDeflate];
return gzData;
}

NSFileWrapper is a new class available in iOS4+ that among other things provides an easy way to serialize entire directory contents. As you can see it’s pretty simple to use here: we just initialize it with a URL, then we can get an NSData representation of a directory by calling serializedRepresentation.

This isn’t compressed, so we use the gzipDeflate helper method from the NSData extensions we downloaded earlier to do that.

3) Implement exportToDiskWithForce

- (BOOL)exportToDiskWithForce:(BOOL)force {
 
[self createDataPath];
 
// Figure out destination name (in public docs dir)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:];
NSString *zippedName = [self getExportFileName];
NSString *zippedPath = [documentsDirectory stringByAppendingPathComponent:zippedName];
 
// Check if file already exists (unless we force the write)
if (!force && [[NSFileManager defaultManager] fileExistsAtPath:zippedPath]) {
return FALSE;
}
 
// Export to data buffer
NSData *gzData = [self exportToNSData];
if (gzData == nil) return FALSE;
 
// Write to disk
[gzData writeToFile:zippedPath atomically:YES];
return TRUE;
 
}

The first thing we do here is construct the full path to where we’re going to save our zipped document. Note this time we’re saving in the Documents directory (not Library\Private Data), because the Documents directory is what’s available for File Sharing.

Next we check to see if the file is already there. If it is, we’re going to want to present a warning to the user, so we return FALSE unless the user forces the save.

Finally, we just make a call to export it as NSData, and simply write it out to the disk.

4) Implement importFromPath

- (BOOL)importData:(NSData *)zippedData {
 
// Read data into a dir Wrapper
NSData *unzippedData = [zippedData gzipInflate];
NSFileWrapper *dirWrapper = [[[NSFileWrapper alloc] initWithSerializedRepresentation:unzippedData] autorelease];
if (dirWrapper == nil) {
NSLog(@"Error creating dir wrapper from unzipped data");
return FALSE;
}
 
// Calculate desired name
NSString *dirPath = [ScaryBugDatabase nextScaryBugDocPath];
NSURL *dirUrl = [NSURL fileURLWithPath:dirPath];
NSError *error;
BOOL success = [dirWrapper writeToURL:dirUrl options:NSFileWrapperWritingAtomic originalContentsURL:nil error:&error];
if (!success) {
NSLog(@"Error importing file: %@", error.localizedDescription);
return FALSE;
}
 
// Success!
self.docPath = dirPath;
return TRUE;
 
}
 
- (BOOL)importFromPath:(NSString *)importPath {
 
// Read data into a dir Wrapper
NSData *zippedData = [NSData dataWithContentsOfFile:importPath];
return [self importData:zippedData];
 
}

We’re actually going to extract most of the work from importFromPath into a helper function called importData, because it will be useful later.

In importData, we just do the opposite of what we did in exportData – we inflate the zipped contents and use NSFileWrapper to expand it again with writeToURL:options:originalContentsURL:error.

As for the destination file name, we just create the next available file name. So we’re never overwriting an existing file when you import, we always create a new file. This is by design to avoid the user from accidentally overwriting their files. If they import the same file twice, they’ll have a duplicate, but they can easily delete files.

Ok – that’s it for the core code. There’s still a bit more to do to integrate into the rest of the app though – we have to make some mods to the ScaryBugDatabase and add some GUI elements to support this.

Integration into App

Just a few steps to integrate this into the rest of the app…

1) Add a helper function to ScaryBugDatabase

We’re going to need a method to return the list of documents that the user can choose to import, so add the following to ScaryBugDatabase.h:

+ (NSMutableArray *)importableScaryBugDocs;

Then implement it in ScaryBugDatabase.m:

+ (NSMutableArray *)importableScaryBugDocs {
 
NSMutableArray *retval = [NSMutableArray array];
 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *publicDocumentsDir = [paths objectAtIndex:];
 
NSError *error;
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:publicDocumentsDir error:&error];
if (files == nil) {
NSLog(@"Error reading contents of documents directory: %@", [error localizedDescription]);
return retval;
}
 
for (NSString *file in files) {
if ([file.pathExtension compare:@"sbz" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
NSString *fullPath = [publicDocumentsDir stringByAppendingPathComponent:file];
[retval addObject:fullPath];
}
}
 
return retval;
 
}

This should be fairly straightforward stuff by now – we just enumerate all of the files in the Documents directory, looking for anything that ends with sbz, and add the full path of anything we find to a NSMutableArray.

2) Modify EditBugViewController to export documents

Make the following changes to EditBugViewController.h:

// Add the UIAlertViewDelegate protocol to the interface declaration
@interface EditBugViewController : UIViewController <UITextFieldDelegate, RateViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIAlertViewDelegate> {

Then make the following changes to EditBugViewController.m:

// Add inside viewDidLoad
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Export" style:UIBarButtonItemStyleBordered target:self action:@selector(exportTapped:)] autorelease];
 
// Add new function
- (void)exportTapped:(id)sender {
[DSBezelActivityView newActivityViewForView:self.navigationController.navigationBar.superview withLabel:@"Exporting Bug..." width:];
[_queue addOperationWithBlock: ^{
BOOL exported = [_bugDoc exportToDiskWithForce:FALSE];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[DSBezelActivityView removeViewAnimated:YES];
if (!exported) {
UIAlertView *alertView = [[[UIAlertView alloc]
initWithTitle:@"File Already Exists!"
message:@"An exported bug with this name already exists. Overwrite?"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Overwrite", nil] autorelease];
[alertView show];
}
}];
}];
}
 
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
 
if (buttonIndex == alertView.firstOtherButtonIndex + ) {
[DSBezelActivityView newActivityViewForView:self.navigationController.navigationBar.superview withLabel:@"Exporting Bug..." width:];
[_queue addOperationWithBlock: ^{
[_bugDoc exportToDiskWithForce:TRUE];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[DSBezelActivityView removeViewAnimated:YES];
}];
}];
}
 
}

Here we just add a new navigation item to our bar with the title “Export”. When the user taps it, we’ll try exporting to disk – and if the file exists present them with a warning, and only overwrite it if they accept.

Note we’re using NSOperationQueues and an activity indicator to make for a nicer user experience for the user. If you’re unsure about how this works, check out the How To Create A Simple iPhone App Tutorial: Part 3/3 tutorial.

3) Create a new view controller to allow user to choose a document to import

We’re going to need a new view controller to list the importable documents so the user can choose one.

We’ll just do something quick and dirty for this. Right click on the View Controllers group, and click “Add\New File…”. Choose UIViewController subclass, and make sure “Targeted for iPad” and “With XIB for user interface” are unchecked, but “UITableViewController subclass” IS checked, and click Next. Name the file ImportBugViewController.m, and click Finish.

Then replace ImportBugViewController.h with the following:

#import <UIKit/UIKit.h>
 
@protocol ImportBugViewControllerDelegate
- (void)importableBugDocTapped:(NSString*)importPath;
@end
 
 
@interface ImportBugViewController : UITableViewController {
NSMutableArray *_importableBugDocs;
id <ImportBugViewControllerDelegate> _delegate;
}
 
@property (copy) NSMutableArray *importableBugDocs;
@property (assign) id <ImportBugViewControllerDelegate> delegate;
 
@end

Note we’re setting up a delegate here so we can notify the root view controller when the user selects a bug to import.

Next make the following changes to ImportBugViewController.m:

// Under @implementation
@synthesize importableBugDocs = _importableBugDocs;
@synthesize delegate = _delegate;
 
// Uncomment viewDidLoad and add the following inside
self.title = @"Import Bug";
 
// Uncomment shouldAutorotateToInterfaceOrientation and set the return value to:
return YES;
 
// Set the return value of numberOfSectionsInTableView to this:
return ;
 
// Set the return value of tableView:numberOfRowsInSection to this:
return _importableBugDocs.count;
 
// In tableView:cellForRowAtIndexPath, add the following after "Configure the cell":
NSString *fullPath = [_importableBugDocs objectAtIndex:indexPath.row];
NSString *fileName = [fullPath lastPathComponent];
cell.textLabel.text = fileName;
 
// Replace the contents of tableView:didSelectRowAtIndexPath with the following:
NSString *fullPath = [_importableBugDocs objectAtIndex:indexPath.row];
[_delegate importableBugDocTapped:fullPath];
 
// Add the following to dealloc
[_importableBugDocs release];
_importableBugDocs = nil;
_delegate = nil;

This should all be pretty standard table view setup to you by now, so no need to discuss this further.

One thing to note though: here we’re displaying just the filename of the object, because to get anything else we’d have to unzip the documents (an expensive operation). However Pages seems to have a thumbnail to go along with their documents. Anyone have any idea how they managed that?

4) Modify RootViewController to have an Import from File Sharing Option

Make the following mods to RootViewController.h:

// At top of file
#import "ImportBugViewController.h"
#import "ScaryBugDatabase.h"
 
// Modify @interface to incldue the UIActionSheetDelegate and ImportBugViewControllerDelegate
@interface RootViewController : UITableViewController <UIActionSheetDelegate, ImportBugViewControllerDelegate> {
 
// Add inside @interface
ImportBugViewController *_importBugViewController;
 
// After @interface
@property (retain) ImportBugViewController *importBugViewController;

Next, make the following changes to RootViewController.m:

// Under @implementation
@synthesize importBugViewController = _importBugViewController;
 
// In didReceiveMemoryWarning
self.importBugViewController = nil;
 
// In dealloc
[_importBugViewController release];
_importBugViewController = nil;
 
// Replace addTapped with this:
- (void)addTapped:(id)sender {
 
UIActionSheet *actionSheet = [[[UIActionSheet alloc]
initWithTitle:@""
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Add New Bug", @"Import Bug", nil] autorelease];
[actionSheet showInView:self.view];
 
}
 
// Add new functions
- (void)addNewDoc:(ScaryBugDoc *)newDoc {
[_bugs addObject:newDoc];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_bugs.count- inSection:];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];
}
 
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
 
if (buttonIndex == actionSheet.firstOtherButtonIndex + ) {
 
ScaryBugDoc *newDoc = [[[ScaryBugDoc alloc] initWithTitle:@"New Bug" rating: thumbImage:nil fullImage:nil] autorelease];
[newDoc saveData];
 
[self addNewDoc:newDoc];
 
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_bugs.count- inSection:];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
 
} else if (buttonIndex == actionSheet.firstOtherButtonIndex + ) {
 
if (_importBugViewController == nil) {
self.importBugViewController = [[[ImportBugViewController alloc] initWithStyle:UITableViewStylePlain] autorelease];
_importBugViewController.delegate = self;
}
_importBugViewController.importableBugDocs = [ScaryBugDatabase importableScaryBugDocs];
[self.navigationController pushViewController:_importBugViewController animated:YES];
 
}
 
}
 
- (void)importableBugDocTapped:(NSString*)importPath {
[self.navigationController popViewControllerAnimated:YES];
ScaryBugDoc *newDoc = [[[ScaryBugDoc alloc] init] autorelease];
if ([newDoc importFromPath:importPath]) {
[self addNewDoc:newDoc];
}
}

So, what we did here was make it so when the user taps the “+” button, instead of just adding a row we ask the user if they want to add a row, or import an existing doc.

If they choose to import a doc, we create our import bug view controller, get the list of importable docs from the ScaryBugDatabase, and pass that onto the controller and dispaly it.

Finally when the user chooses a doc, we call the importFromPath method we wrote earlier, and add the document.

One last thing then we’re done!

5) Enable File Sharing in the Info.plist

Finally open up ScaryBugs-Info.plist and add a new boolean key called UIFileSharingEnabled and make sure it’s checked:

Phew! It was a long, crazy process but we’re finally done and get to enjoy the fruits of our labor!

Trying It Out

Build and run your project, and make sure you install it on your iPhone (i.e. not your Simulator), because File Sharing only works with iTunes on a physical device.

You can test that the OS detected your “UIFileSharingEnabled” flag by loading up iTunes, switching to the Apps Tab, and scrolling down: you should see your app there as long as your device is connected:

If it doesn’t show up, try deleting the app from your device and re-installing, and synching iTunes.

Next go ahead and create a new bug, and tap Export. You should immediately see a new entry in the Scary Bug Documents in iTunes File Sharing:

Now for some fun. Download a copy of a sample bug I made. Then in File Sharing in iTunes click “Add…” and browse to the file you downloaded.

Finally, go to the table view in Scary Bugs, tap the “+” button, and tap “Import Bug”. Choose the file you downloaded and you should see a new bug appear on your screen!

Where To Go From Here?

Attached is a sample project with the code we’ve developed in the above iTunes tutorial.

Theres still a little bit of life left in these bugs! Next up is a tutorial on how to import and export your bug documents via email – which will be a snap considering all of the groundwork we’ve laid so far!

Itunes共享机制实现的更多相关文章

  1. 使用WMI来控制Windows目录 和windows共享机制

    1.使用WMI来控制Windows目录 本文主要介绍如何使用WMI来查询目录是否存在.文件是否存在.如何建立目录.删除目录,删除文件.如何利用命令行拷贝文件,如何利用WMI拷贝文件 using Sys ...

  2. ActiveMQ新的Master/Slave存储共享机制Replicated LevelDB Store

    ActiveMQ新的Master/Slave存储共享机制Replicated LevelDB Store 使用ZooKeeper协调选择一个node作为master.被选择的master broker ...

  3. Android背后的设计思想——功能共享机制

    Android的系统设计,与别的智能手机操作系统有很大区别,甚至在以往的任何操作系统里,很难找到像Android这样进行全面地系统级创新的操作系统.从创新层面上来说,Android编程上的思想和支持这 ...

  4. linux下的KSM内存共享机制分析

    2017-04-26 KSM是内核中的一种内存共享机制,在2.6.36版本的内核中开始引入,简单来说就是其会 合并某些相同的页面以减少页面冗余.在内核中有一个KSM守护进程 ksmd,它定期扫描用户向 ...

  5. 分布式session共享机制分析

    使用配置: 1.在pom文件中引入spring-session的jar包 <!--springsession--><dependency><groupId>org. ...

  6. unittest的前置后置,pytest的fixture和共享机制conftest.py

    Unittest setUp/tearDown setUp当中得到的变量,用self.xxx = value传递给测试用例 setUpClass/tearDownClass setupClass当中得 ...

  7. 大叔也说Xamarin~Android篇~为HttpClient共享Session,android与api的session共享机制

    回到目录 杂谈 在进行android进行开发时,我们的数据一般通过接口来获收,这里指的接口泛指web api,webservice,wcf,web应用程序等:它们做为服务端与数据库进行直接通讯,而AP ...

  8. Qt隐式共享机制

    1.浅拷贝 浅拷贝-引用类型.浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同),对其中任何一个对象的改动都会影响另外一个对象. 2.深拷贝 而深拷贝-值类型.深拷贝是指源对象与 ...

  9. JS的prototype的共享机制分析

    function Super(){ } Super.prototype.aaa=[1,2,3]; Super.prototype.bbb=1; function Sub(){ Super.call(t ...

随机推荐

  1. C#高级参数out,ref,params

    在C#中通过使用方法来获取返回值时,通常只能得到一个返回值.因此,当一个方法需要返回多个值的时候,就需要用到ref和out,那么这两个方法区别在哪儿呢? out 当需要返回一系列返回值时可用out高级 ...

  2. python-re使用举例

    代码: import re text = "JGood is a handsome boy, he is cool, clever, and so on..." print(tex ...

  3. E20190303-hm

    invoke vt. 乞灵,祈求; 提出或授引…以支持或证明; 召鬼; 借助;

  4. 1090 Highest Price in Supply Chain (25 分)

    A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone invo ...

  5. Lightoj1081【500棵线段树维护】

    #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=5e2+10; const ...

  6. HDU2489【状压枚举】

    题意: 给你n个点的图,然后让你在图里挑m个点,达到sumedge/sumnode最小 思路: 由于数据范围小,状压枚举符合m个点的状态,我是用vactor存了结点位置,也记录了结点的sum值,然后跑 ...

  7. uoj#283. 直径拆除鸡(构造)

    传送门 好神的构造题 vfk巨巨的题解 //minamoto #include<bits/stdc++.h> #define R register #define fp(i,a,b) fo ...

  8. 在 UIViewController 中手动增加 TableView 出现 Type 'SomeViewController' does not confirm to protocol 'UITableViewDataSource' 问题的解决办法

    许多时候我们都有在普通的继承自 UIViewController 的控制器中使用 TableView 的需求,这时候就需要当前控制器类继承 UITableViewDelegate 和 UITableV ...

  9. C# 基础之字段与属性

    1.属性是字段的扩展 2.根据面向对象封装思想,字段最好设为private(私有),这样有利于防止客户端对字段的篡改,从而保证了成员的完整性 3.访问类中私有字段,C#提供了属性,用来对字段进行灵活的 ...

  10. C# 可空类型(Nullable)

    C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值. 例如,Nullable< Int32 >,读作& ...